JDK-8173654 : Regression since 8u60: System.getenv doesn't return env var set in JNI code
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 8u60
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows
  • Submitted: 2017-01-30
  • Updated: 2018-02-20
  • Resolved: 2017-06-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 10 JDK 8
10 b12Fixed 8u131Fixed
After upgrade to Java or higher),the System.getenv ()
doesn't return the variables that the application set at run time.  
Those environment variables are set through JNI interface (Java call C++ to
set environment variables) at run time. 
This seems to be a regression since 8u60 as 8u51 works.

20 years ago I was annoyed that Java didn't support chdir, but I eventually accepted it as good practice. Later, when we designed environment variable support, the cache-on-first-access was an explicit decision. But it doesn't appear in the spec. There is always an unspecified mapping to whatever the native platform does, and there was never a good place to document such platform-dependent implementation decisions. There is no promise of perfect fidelity to native environment variables, and in fact such fidelity is unimplementable.

Martin: it probably was not clear but the current problem is on Windows where there are no such warnings regarding changing the process environment. The current spec does not infer a static snapshot is taken of the env on first call. The implementation is simply wrong in my opinion.

You can argue that the spec is technically correct, the same way C++ compiler writers justify actions when code triggers undefined behavior. http://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html says """The putenv() function need not be thread-safe.""" I agree the current wording is a little misleading, but we shouldn't promise the current behavior. It should be valid to wrap the current environment in a SoftReference and refetch on memory pressure, for example. It would also be misleading to suggest that native modifications to the environment are simply not incorporated, since a crash is also quite possible (and have been observed in practice at Google!)

sounds like we might have to update the specification for this method then. It's misleading : https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getenv-- states "Returns an unmodifiable string map view of the current system environment."

On Unix, setting environment variables is not thread safe. Really! Setting environment variables via JNI is completely unsupported and may crash your process! But independent of that, the caching of the environment on first use (and its immutability except when creating a subprocess) was a deliberate design decision back in the 5.0 days. So no JDK bug here. OTOH ... I don't think the caching behavior was ever specified, and it might be useful to users to know the rules.

I've added Martin to the Watchers as I believe he has had a lot of involvement with this in the past (eg restoring getenv in JDK 5.0) and may be able to shed some light on the caching behaviour that now exists.

I think I found the code change responsible for this behavioural change. Through an instrumented binary, I see that usage tracker is now invoking the ProcessEnvironment early in our stack. 25471110 dignostics - loading ProcessEnvironment java.lang.Throwable at java.lang.ProcessEnvironment.<clinit>(ProcessEnvironment.java:76) at java.lang.System.getenv(System.java:897) at sun.usagetracker.UsageTrackerClient$2.run(UsageTrackerClient.java:146) at sun.usagetracker.UsageTrackerClient$2.run(UsageTrackerClient.java:144) at java.security.AccessController.doPrivileged(Native Method) at sun.usagetracker.UsageTrackerClient.getEnvPrivileged(UsageTrackerClient.java:144) at sun.usagetracker.UsageTrackerClient.getOSSpecificConfigFilePath(UsageTrackerClient.java:192) at sun.usagetracker.UsageTrackerClient.getConfigFilePrivileged(UsageTrackerClient.java:163) at sun.usagetracker.UsageTrackerClient.setupAndTimestamp(UsageTrackerClient.java:295) at sun.usagetracker.UsageTrackerClient.access$000(UsageTrackerClient.java:78) at sun.usagetracker.UsageTrackerClient$4.run(UsageTrackerClient.java:322) at sun.usagetracker.UsageTrackerClient$4.run(UsageTrackerClient.java:317) at java.security.AccessController.doPrivileged(Native Method) at sun.usagetracker.UsageTrackerClient.run(UsageTrackerClient.java:317) at sun.misc.PostVMInitHook.trackJavaUsage(PostVMInitHook.java:29) at sun.misc.PostVMInitHook.run(PostVMInitHook.java:21)

I don't see anything related in 8066504. I would suspect perhaps a client change, or a security libs change - given the uses of System.getenv in the codebase.

I'll have to do some binary chop testing to pinpoint exact build where issue started to be seen. I do see one possible related 8u60 fix in java_props_md.c : JDK-8066504 - Not sure yet if it's a factor.

The System.getenv functionality is implemented through the helper class ProcessEnvironment and has not changed in 8u60. This class caches the environment content upon first call. I can only suspect that an unrelated change is causing the first call to System.getenv to happen earlier than before - and importantly before the JNI code has set the variables expected to be written. I find this caching action to be questionable, as the API specification says nothing about such behaviour, but it has been this way for quite some time. In any case this is not in any way a hotspot->runtime issue so I am moving it to core-libs->java.lang