Loading a DLL with LoadLibraryA(path_to_dll) is changing the inherit handle flag (HANDLE_FLAG_INHERIT) from 1 to 0 for file descriptors 0, 1, and 2

huangapple go评论94阅读模式
英文:

Loading a DLL with LoadLibraryA(path_to_dll) is changing the inherit handle flag (HANDLE_FLAG_INHERIT) from 1 to 0 for file descriptors 0, 1, and 2

问题

我们已经使用golang编写了一些函数,并在其上面构建了一个C包装器来调用这些函数。我们首先构建golang代码以创建一个存档文件,然后构建C中的包装器代码以作为DLL使用。

在我的程序中使用LoadLibraryA(path_to_dll)加载此DLL后,我发现fd 0、1和2的继承标志从1变为0。尽管这不是在加载DLL后立即发生的。我在加载库之后的代码中添加了sleep,并且似乎在加载库后几毫秒后才更改标志值。

我使用GetHandleInformation((HANDLE) sock, &flags)来获取继承标志值。

对于可能导致这种情况的原因,有什么想法/指示吗?谢谢!

更新:
我已经找到了在go代码中导致继承标志值翻转的确切行。下面的k8sService.go代码中的全局变量reqHandler导致了这个问题。你知道为什么使用这个全局变量会导致继承标志值翻转吗?

my-lib/k8sService.go(go代码)

package main

import "C"
import (
	"my-lib/pkg/cmd"
)

func main() {
}

var reqHandler []*cmd.K8sRequest

my-lib/pkg/cmd/execute.go

import (
	"my-lib/pkg/dto"
)

type K8sRequest struct {
	K8sDetails dto.K8sDetails
}

my-lib/pkg/dto/structs.go

package dto

// K8sDetails包含与集群通信的所有必要信息。下面的结构体还有几个变量。
type K8sDetails struct {
	// 集群的API服务器的主机名
	HostName string `json:"hostname"`
	// API服务器监听的端口
	Port int `json:"port"`
}

我们在上述k8sService.go之上有一个C包装器。我们首先构建golang代码以创建一个存档文件,然后使用此存档文件和C中的包装器代码构建目标DLL。以下是加载此DLL并在加载DLL之前和之后打印继承标志值的示例程序。

#include <windows.h>
#include <iostream>
#include <io.h>
#include "wrapper/cWrapper.h"

void printInheritVals() {
		typedef SOCKET  my_socket_t;
		my_socket_t fd0 = _get_osfhandle(0);
		my_socket_t fd1 = _get_osfhandle(1);
		my_socket_t fd2 = _get_osfhandle(2);
		std::cout << "fd0: " << fd0 << std::endl;
		std::cout << "fd1: " << fd1 << std::endl;
		std::cout << "fd2: " << fd2 << std::endl;
		
		
		DWORD flags;
		int inherit_flag_0 = -1;
		int inherit_flag_1 = -1;
		int inherit_flag_2 = -1;
		

       	if (!GetHandleInformation((HANDLE) fd0, &flags)) {
			std::cout << "GetHandleInformation failed" << std::endl;
    	} else {
    		inherit_flag_0 = (flags & HANDLE_FLAG_INHERIT);
    	}

       	if (!GetHandleInformation((HANDLE) fd1, &flags)) {
			std::cout << "GetHandleInformation failed" << std::endl;
    	} else {
    		inherit_flag_1 = (flags & HANDLE_FLAG_INHERIT);
    	}

		if (!GetHandleInformation((HANDLE) fd2, &flags)) {
			std::cout << "GetHandleInformation failed" << std::endl;
    	} else {
    		inherit_flag_2 = (flags & HANDLE_FLAG_INHERIT);
    	}
		
		std::cout << "inherit_flag_0: " << inherit_flag_0 << std::endl;
		std::cout << "inherit_flag_1: " << inherit_flag_1 << std::endl;
		std::cout << "inherit_flag_2: " << inherit_flag_2 << std::endl;
}

int main()
{

	printInheritVals(); // 输出中所有标志值都为1
	HINSTANCE hGetProcIDDLL = LoadLibraryA(PATH_TO_DLL);
	if (!hGetProcIDDLL) {
		std::cout << "无法加载动态库" << std::endl;
		return EXIT_FAILURE;
	}
	std::cout << "库已加载" << std::endl;
	printInheritVals(); // 输出中所有标志值都为1
	Sleep(1000);
	printInheritVals(); // 输出中所有标志值都为0
	return EXIT_SUCCESS;
}
英文:

We have written some functions in golang and a c wrapper on top of that to invoke those functions. We first build golang code to create an archive file and then we build the wrapper code in c to be consumed as a DLL.

After loading this DLL using LoadLibraryA(path_to_dll) in my program I am seeing that the inherit flags for fd 0, 1, and 2 are getting changed from 1 to 0. This does not happen immediately after loading the DLL though. I had added sleep in my code after the load library call and seems like it takes a few milliseconds after loading the library to change the flag values.

I am using GetHandleInformation((HANDLE) sock, &flags) to get the inherit flag value.

Any idea/pointers on what could be causing this? Thanks!

Updates:
I was able to find out the exact line in the go code that is flipping the inherit flag values. The global variable reqHandler in below k8sService.go code is causing this. Any idea why the use of this global variable is flipping the inherit flag values?

my-lib/k8sService.go (go code)

package main

import &quot;C&quot;
import (
	&quot;my-lib/pkg/cmd&quot;
)

func main() {
}

var reqHandler []*cmd.K8sRequest

my-lib/pkg/cmd/execute.go

import (
	&quot;my-lib/pkg/dto&quot;
)

type K8sRequest struct {
	K8sDetails dto.K8sDetails
}

my-lib/pkg/dto/structs.go

package dto

// K8sDetails contains all the necessary information about talking to the cluster. Below struct has few more variables.
type K8sDetails struct {
	// HostName of the cluster&#39;s API server
	HostName string `json:&quot;hostname&quot;`
	// Port on which the API server listens on to
	Port int `json:&quot;port&quot;`
}

We have a C wrapper on top of the above k8sService.go. We first build golang code to create an archive file and then with this archive file and wrapper code in C we build the target DLL. Below is the sample program which loads this DLL and also prints the inherit flag values before and after loading the DLL.

#include &lt;windows.h&gt;
#include &lt;iostream&gt;
#include &lt;io.h&gt;
#include &quot;wrapper/cWrapper.h&quot;

void printInheritVals() {
		typedef SOCKET  my_socket_t;
		my_socket_t fd0 = _get_osfhandle(0);
		my_socket_t fd1 = _get_osfhandle(1);
		my_socket_t fd2 = _get_osfhandle(2);
		std::cout &lt;&lt; &quot;fd0: &quot; &lt;&lt; fd0 &lt;&lt; std::endl;
		std::cout &lt;&lt; &quot;fd1: &quot; &lt;&lt; fd1 &lt;&lt; std::endl;
		std::cout &lt;&lt; &quot;fd2: &quot; &lt;&lt; fd2 &lt;&lt; std::endl;
		
		
		DWORD flags;
		int inherit_flag_0 = -1;
		int inherit_flag_1 = -1;
		int inherit_flag_2 = -1;
		

       	if (!GetHandleInformation((HANDLE) fd0, &amp;flags)) {
			std::cout &lt;&lt; &quot;GetHandleInformation failed&quot; &lt;&lt; std::endl;
    	} else {
    		inherit_flag_0 = (flags &amp; HANDLE_FLAG_INHERIT);
    	}

       	if (!GetHandleInformation((HANDLE) fd1, &amp;flags)) {
			std::cout &lt;&lt; &quot;GetHandleInformation failed&quot; &lt;&lt; std::endl;
    	} else {
    		inherit_flag_1 = (flags &amp; HANDLE_FLAG_INHERIT);
    	}

		if (!GetHandleInformation((HANDLE) fd2, &amp;flags)) {
			std::cout &lt;&lt; &quot;GetHandleInformation failed&quot; &lt;&lt; std::endl;
    	} else {
    		inherit_flag_2 = (flags &amp; HANDLE_FLAG_INHERIT);
    	}
		
		std::cout &lt;&lt; &quot;inherit_flag_0: &quot; &lt;&lt; inherit_flag_0 &lt;&lt; std::endl;
		std::cout &lt;&lt; &quot;inherit_flag_1: &quot; &lt;&lt; inherit_flag_1 &lt;&lt; std::endl;
		std::cout &lt;&lt; &quot;inherit_flag_2: &quot; &lt;&lt; inherit_flag_2 &lt;&lt; std::endl;
}

int main()
{

	printInheritVals(); // In output all flag values are 1
	HINSTANCE hGetProcIDDLL = LoadLibraryA(PATH_TO_DLL);
	if (!hGetProcIDDLL) {
		std::cout &lt;&lt; &quot;could not load the dynamic library&quot; &lt;&lt; std::endl;
		return EXIT_FAILURE;
	}
	std::cout &lt;&lt; &quot;Library loaded&quot; &lt;&lt; std::endl;
	printInheritVals(); // In output all flag values are 1
	Sleep(1000);
	printInheritVals(); // In output all flag values are 0
	return EXIT_SUCCESS;
}

答案1

得分: 1

这是golang.org/x/sys/windows包中的一个错误。相同的问题曾经也存在于内置的syscall包中,但在Go 1.17中已经修复

你的项目中可能导入了golang.org/x版本的包,而不是内置的包,所以下面的代码会执行以初始化StdinStdoutStderr变量:

var (
	Stdin  = getStdHandle(STD_INPUT_HANDLE)
	Stdout = getStdHandle(STD_OUTPUT_HANDLE)
	Stderr = getStdHandle(STD_ERROR_HANDLE)
)

func getStdHandle(stdhandle uint32) (fd Handle) {
	r, _ := GetStdHandle(stdhandle)
	CloseOnExec(r)
	return r
}

修复这段代码的方法是移除CloseOnExec调用,这会清除给定文件句柄上的HANDLE_FLAG_INHERIT标志。

如何在你的项目中解决这个问题不太清楚。我认为你可以在项目中使用_vendor_方式引入golang.org/x/sys模块,可能需要在go.mod文件中使用replace指令。然后在本地副本中应用修复。

同时,我鼓励你报告这个错误。文档指导你在GitHub上的主Go项目上报告问题,标题前缀为x/sys

英文:

This is a bug in the golang.org/x/sys/windows package. The same issue used to be in the built-in syscall package as well, but it was fixed in Go 1.17.

Something in your project must be importing the golang.org/x version of the package instead of the built-in one, and so the following code executes to initialize the Stdin, Stdout, and Stderr variables:

var (
	Stdin  = getStdHandle(STD_INPUT_HANDLE)
	Stdout = getStdHandle(STD_OUTPUT_HANDLE)
	Stderr = getStdHandle(STD_ERROR_HANDLE)
)

func getStdHandle(stdhandle uint32) (fd Handle) {
	r, _ := GetStdHandle(stdhandle)
	CloseOnExec(r)
	return r
}

The fix for that code would be to remove the CloseOnExec call, which is what clears HANDLE_FLAG_INHERIT on the given file handle.

How to solve that in your project is less clear. I think you can vendor golang.org/x/sys module in your project, perhaps with a replace directive in your go.mod. Apply the fix in your local copy.

Meanwhile, I encourage you to also report the bug. The documentation instructs you to report the issue on the main Go project at GitHub, prefixing the title with x/sys.

huangapple
  • 本文由 发表于 2022年4月20日 09:41:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/71933185.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定