64 位 Windows API 结构对齐导致命名管道上的访问被拒绝错误 [英] 64-bit Windows API struct alignment caused Access Denied error on named pipe

查看:19
本文介绍了64 位 Windows API 结构对齐导致命名管道上的访问被拒绝错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了两天时间,但我终于缩小了在尝试将 CallNamedPipe 作为结构对齐问题时 ERROR_ACCESS_DENIED (5) 错误的来源.我们有一个 32 位服务和一个 32 位应用程序,我正在尝试将服务更新为 64 位服务.奇怪的是,在 32 位模式下一切正常,但在 64 位模式下,来自 32 位应用程序的 CallNamedPipe 报告访问被拒绝错误.

It has taken me two days, but I've finally narrowed down the source of an ERROR_ACCESS_DENIED (5) error when attempting to CallNamedPipe as a problem with structure alignment. We had a 32-bit service and a 32-bit application, and I am trying to update the service to be a 64-bit service. The strange thing is that everything was working fine in 32-bit mode, but in 64-bit mode CallNamedPipe from the 32-bit application was reporting an access denied error.

该服务已经设置了一个 SECURITY_ATTRIBUTES 结构,并使用正确初始化的 PSECURITY_DESCRIPTOR 填充 lpSecurityDescriptor 成员.这在传递给 CreateNamedPipe 时没有报告任何错误.我仍然不知道为什么它没有报告错误;也许不好的安全属性会默默地回退到默认值而不是失败.

The service was already setting up a SECURITY_ATTRIBUTES structure and populating the lpSecurityDescriptor member with a properly initialized PSECURITY_DESCRIPTOR. And this wasn't reporting any errors when passed to CreateNamedPipe. I still don't know why it wasn't reporting an error; maybe bad security attributes silently falls back to a default instead of failing.

经过多次折腾(包括之前一些不完整/不正确的改变结构对齐的尝试——调试服务启动代码并不容易),我开始意识到将默认结构对齐设置为1字节(/Zp1)的项目设置是造成问题.当我最终在 #include #include #pragma pack(pop) 出现之前使用了 #pragma pack(push,8)代码>之后,然后事情开始工作.

Through many gyrations (including some earlier incomplete/incorrect attempts at changing structure alignment - debugging service startup code is not easy), I came to realize that the project setting that sets the default struct alignment to 1 byte (/Zp1) was causing problems. When I finally used #pragma pack(push,8) before all occurrences of #include <windows.h> and #pragma pack(pop) afterward, then things started working.

我现在的问题是为什么这是必要的.我看到Windows API中有很多头文件通过包含pshpack1.hpshpack2.hpshpack4.h来显式设置结构对齐>、pshpack8.hpoppack.h.我如何知道 Windows API 何时控制自己的打包以及何时我有责任设置正确的打包级别?不是每个关心结构对齐的 Windows API 声明都应该设置正确的包装,这样我就不必筛选系统中的所有代码,包括 Windows API 头文件吗?一种替代方法是更改​​项目设置以使用默认结构对齐,但我必须假设这样做是因为我们的系统中依赖 1 字节结构对齐的代码比依赖 Windows API 的代码更多.

My question now is why is this necessary. I see that there are many header files in the Windows API that explicitly set structure alignment by including pshpack1.h, pshpack2.h, pshpack4.h, pshpack8.h and poppack.h. How am I to know when the Windows API controls its own packing and when it becomes my responsibility to have the proper pack level set? Shouldn't every Windows API declaration that cares about structure alignment set the proper packing so I don't have to sift through all the code in the system for anything including Windows API header files? One alternative would be to change the project setting to use default structure alignment, but I have to assume that this was done because we have even more code in our system relying on 1-byte structure alignment than we have relying on the Windows API.

这是服务器端代码的样子:

This is what the server side code looks like:

BOOL OpenMyPipe()
{
   SECURITY_ATTRIBUTES sa;
   PSECURITY_DESCRIPTOR pSD;

   printf("sizeof(SECURITY_ATTRIBUTES) == %d\n", sizeof(SECURITY_ATTRIBUTES));
   pSD = (PSECURITY_DESCRIPTOR)GlobalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
   if (pSD == NULL)
      return FALSE;

   if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
      return FALSE;

   if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL)NULL, FALSE))
      return FALSE;

   sa.nLength = sizeof(sa);
   sa.lpSecurityDescriptor = pSD;
   sa.bInheritHandle = FALSE;

   char szPipeName[MAX_PATH];
   sprintf(szPipeName, "\\\\.\\pipe\\%s%s", "__SQLTST_",
      "MAINMR");

   hPipe = CreateNamedPipe(szPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
      1, 0, 0, NMPWAIT_WAIT_FOREVER, &sa);

   if (hPipe == INVALID_HANDLE_VALUE)
      return FALSE;
   return TRUE;
}

为简单起见,我用一个小型 VB.NET 客户端验证了这一点:

For simplicity's sake I verified this with a small VB.NET client:

Sub Main()
  Dim pipes = System.IO.Directory.GetFiles("\\.\pipe\")

  Using pipe As New System.IO.Pipes.NamedPipeClientStream(".", "__SQLTST_MAINMR")
     Dim message(16) As Byte
     pipe.Connect(3000)
     Array.Copy(BitConverter.GetBytes(Process.GetCurrentProcess().Id), message, 4)
     pipe.Write(message, 0, 16)
  End Using
End Sub

我相信只有在服务器端代码在不同的帐户(如 SYSTEM 帐户)下运行时才会发生错误.不过,我不知道如何轻松测试.我所知道的是,当上面的代码都在与常规应用程序代码相同的帐户下运行时,即使没有设置 SECURITY_ATTRIBUTES,它也不会失败.另外,当然,你必须在服务器代码中将结构对齐设置为1字节才能看到错误.

I believe the error only occurs when the server side code is running under a different account like the SYSTEM account. I don't know how to easily test that, though. What I do know is that the above code will not fail even with no SECURITY_ATTRIBUTES set up when it's all running under the same account as regular application code. Also, of course, you have to set the structure alignment in the server code to 1 byte to see the error.

推荐答案

Windows SDK 要求打包为 8 个字节.来自 使用 Windows 标头

The Windows SDK expects packing to be 8 bytes. From Using the Windows Headers

项目应该被编译为使用默认的结构打包,当前是 8 字节,因为最大的整数类型是 8 字节.这样做可确保头文件中的所有结构类型都以 Windows API 期望的相同对齐方式编译到应用程序中.它还确保具有 8 字节值的结构正确对齐,并且不会在强制执行数据对齐的处理器上导致对齐错误.

Projects should be compiled to use the default structure packing, which is currently 8 bytes because the largest integral type is 8 bytes. Doing so ensures that all structure types within the header files are compiled into the application with the same alignment the Windows API expects. It also ensures that structures with 8-byte values are properly aligned and will not cause alignment faults on processors that enforce data alignment.

这是确保数据结构按照系统预期对齐的必要条件.我怀疑没有明确这样做的原因是他们想要默认值,所以 为什么要别的东西.更换包装相对少见,应仅限于特定情况.如果微软将 #pragma pack(push,8) 添加到每个头文件中,他们会隐含地说改变对齐方式是正常的.

This is necessary to ensure that data structures are aligned as the system expects. I suspect the reason for not doing it explicitly is that they want the default, so why ask for anything else. Changing the packing is relatively rare and should be confined to specific circumstances. If Microsoft added in #pragma pack(push,8) to every header file, they would be implicitly saying it is normal to change alignment.

未对齐的结构节省空间,但会降低性能,因为 对齐错误是在访问未对齐的成员时产生的.

Unaligned structures save space, but reduce performance as alignment faults are generated when accessing unaligned members.

出于多种原因,Windows SDK 确实会更改结构的对齐方式.一种可能用于需要读取 32 位或 64 位数据结构的文件格式.例如,可以使用 IMAGE_THUNK_DATA64IMAGE_THUNK_DATA32 读取 PE 文件格式.前者需要 8 字节填充,而后者需要 4 字节填充.类似地,Wininet.h 将根据是为 32 位还是 64 位代码编译不同的数据结构.这些是包装方面的合法更改,但有特定原因.

The Windows SDK does change alignments for structures for a number of reasons. One might be for file formats that need to read either 32 or 64-bit data structures. For example, the PE-file format can be read using either IMAGE_THUNK_DATA64 or IMAGE_THUNK_DATA32. The former needs 8 byte padding whilst the latter needs 4 byte padding. Similarly, Wininet.h will pack the data structures differently depending on whether it is being compiled for 32-bit or 64-bit code. These are legitimate changes in packing, but with a specific reason.

这篇关于64 位 Windows API 结构对齐导致命名管道上的访问被拒绝错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆