go: net: Listen fails on Windows when SYSTEMROOT environment variable is not set

Under Windows, net.Listen fails with the error message socket: The requested service provider could not be loaded or initialized. when the SYSTEMROOT variable is not set. This can e. g. be achieved by running an exec.Command with a non-nil Env that doesn’t contain the parent process’s environment.

I have written a sample application demonstrating the issue under https://github.com/Xjs/envless-listen.

This issue can be reproduced with go version go1.9 windows/amd64 under Windows 10.

According to https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx, this error occurs if some DLL cannot be loaded, so I assume a Windows socket syscall needs to load a DLL which is not found if SYSTEMROOT is unset.

I don’t know how this issue is best mitigated. I don’t even know if the leverage point is net or rather os/exec. One could imagine something like exec.Cmd never omitting SYSTEMROOT from the environment on Windows, even when passing a non-nil Env. However, if it is possible to infer the system root from some other source, the net package might also consider re-setting it. It might also be fine just leaving it as-is and documenting somewhere that sockets under Windows require access to that library.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 18 (17 by maintainers)

Most upvoted comments

I found some MS reference about sub-process creation, cf. under section lpEnvironment, https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa

It appears that Go implements correctly the expected behavior because if cmd.Env is left to nil, the environment variables are inherited from the parent process. Assigning an empty array as in the example, imposes explicitly to start the sub-process with an empty set of environment variables.

The relevant part becomes:

        env := []string{}
	cmd := exec.Command("listener")
	switch {
	case working:
		env = append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
		cmd.Env = env
	case failing:
		// nothing assigned and Env remains nil
		// failing means only that SystemRoot was not explicitly assigned.
	}

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()

Bugs like this one are why I filed #25210