安全属性是什么时候发生变化的,为什么会变化?

4
我有一些代码,使用P/Invoke启动进程并捕获标准输出。 (为什么要使用P/Invoke而不是System.Diagnostics.Process的故事很长而且复杂;可以说这是一个要求。)它在生产环境下高负载下运行了将近一年,并且测试始终通过。
然而,今天早上我运行测试时,它们失败了。我无法确定上次运行测试的确切时间(5/15/2014),但我认为是4/24/2014。当时测试通过了,但今天早上测试失败了。我收到了“PInvokeStackImbalance”错误消息,因此我进行了一些研究,最终意识到extern方法(在这种情况下是CreatePipe)使用的某个结构体的签名不正确。我更改了它,然后测试又开始通过了。
我很高兴找到了解决方法,但我对部署感到担忧。为什么结构体的签名会发生变化呢?我没有升级我的操作系统或其他任何东西 - 我在4/24上运行的是Windows 7 x64,现在仍然在运行。 (部署环境是Windows Server 2012。)自那以后,我安装(和卸载)了一些轻量级的第三方工具,而不是Microsoft或系统组件。我认为Windows更新的热补丁应该是原因,但我无法确定是哪一个。
明确地说,在我的代码中,我仅更改了这个:
    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public UInt32 nLength;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

转换为:

    [StructLayout(LayoutKind.Sequential)]
    internal class SECURITY_ATTRIBUTES
    {
        public int nLength = 12;
        public IntPtr lpSecurityDescriptor = IntPtr.Zero;
        public bool bInheritHandle;
    }


我需要确保我在本地机器上使代码工作的更改不会在部署到生产环境时导致应用程序出现故障。有人知道如何确定什么需要这种更改以及如何确定生产环境是否需要它吗?

编辑:

以下是打开标准输出管道的代码:

    private PipeInfo CreatePipe()
    {
        PipeInfo pipeInfo = new PipeInfo();

        SafeFileHandle safeFileHandle = null;
        try
        {
            Native.SECURITY_ATTRIBUTES pipeAttributes = new Native.SECURITY_ATTRIBUTES();
            pipeAttributes.bInheritHandle = true;
            if (!Native.CreatePipe(out safeFileHandle, out pipeInfo.ChildHandle, pipeAttributes, 0) || safeFileHandle.IsInvalid || pipeInfo.ChildHandle.IsInvalid)
            {
                throw new Win32Exception();
            }

            if (!Native.DuplicateHandle(new HandleRef(this, Native.GetCurrentProcess()), safeFileHandle, new HandleRef(this, Native.GetCurrentProcess()), out pipeInfo.ParentHandle, 0, false, 2))
            {
                throw new Win32Exception();
            }
        }
        finally
        {
            if (safeFileHandle != null && !safeFileHandle.IsInvalid)
            {
                safeFileHandle.Close();
            }
        }

        return pipeInfo;
    }

这段代码并非完全由我编写,我主要是从.NET Reference Source中借鉴的。

时间线如下:

  • 2013年5月 - 编写了CreatePipe代码,第一个版本的SECURITY_ATTRIBUTES
  • 2013年6月 - 部署;代码从那时起一直成功运行
  • 2014年4月 - 没有进行任何更改,代码开始抛出堆栈不平衡错误
  • 2014年5月 - 我更改为第二个版本的SECURITY_ATTRIBUTES,错误消失了

2
SECURITY_ATTRIBUTES从未改变过。你真的不应该使用那个神奇的常量12。使用Marshal.SizeOf。我猜你在代码中改变了其他东西。请查看您的修订控制历史记录以找出原因。 - David Heffernan
12 是 x64 的错误值。 - Raymond Chen
此外,您所描述的更改不会改变您是否出现堆栈不平衡的情况。因此,您确实需要搞清事实。 - David Heffernan
1
@DavidHeffernan,我知道解决方案本身的代码没有改变,这就是为什么我怀疑一个热修复程序更改了解决方案底层依赖关系的原因。此外,事实是,第一段代码块产生了堆栈不平衡,但只需按照所描述的方式更改此结构/类,错误就会消失。所以事实是明确的,只是它们不符合逻辑;因此我的问题。(感谢您提醒不要使用魔术常量; 我会考虑使用 SizeOf。) - nateirvin
你能展示一下出现栈不平衡的代码吗? - David Heffernan
1
所以,我从未找出发生了什么。最后,我告诉我们的部署团队,我们需要在服务器上安装KB2958732KB2964358,然后我们成功地部署到QA和Production。我的理论是,某个热修复程序使nLength不再设置为默认值,因此我必须在我的代码中执行它,这可能更好。 - nateirvin
1个回答

1
我们在x64上遇到了这个问题,而这篇文章是我们搜索结果的首选。我们使用了像你的解决方案一样的神奇数字12作为nLength参数,这个数字来源于C#的进程源代码:https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs
    [StructLayout(LayoutKind.Sequential)]
    internal class SECURITY_ATTRIBUTES {
    #if !SILVERLIGHT
        // We don't support ACL's on Silverlight nor on CoreSystem builds in our API's.  
        // But, we need P/Invokes to occasionally take these as parameters.  We can pass null.
        public int nLength = 12;
        public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
        public bool bInheritHandle = false;
    #endif // !SILVERLIGHT
    }

原来CreatePipe函数需要一个指针,参考文档:

lpPipeAttributes

一个SECURITY_ATTRIBUTES结构体的指针,决定返回的句柄是否可以被子进程继承。如果lpPipeAttributes为NULL,则该句柄无法被继承。

解决方案详见stackoverflow帖子。它适用于x86和x64。我们的代码基于那个stackoverflow帖子和进程源代码(在顶部使用DWORD = System.UInt32;)。

internal static class NativeMethods
{

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
        IntPtr lpPipeAttributes, int nSize);
        
    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, SafeHandle hSourceHandle,
        IntPtr hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess,
        bool bInheritHandle, int dwOptions);
        
    [StructLayout(LayoutKind.Sequential)]
    public struct PIPE_SECURITY_ATTRIBUTES
    {
        public DWORD nLength;
        public IntPtr lpSecurityDescriptor;
        [MarshalAs(UnmanagedType.Bool)]
        public bool bInheritHandle;
    }

    public static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
    {
        PIPE_SECURITY_ATTRIBUTES lpPipeAttributes = new PIPE_SECURITY_ATTRIBUTES();
        lpPipeAttributes.nLength = (DWORD)Marshal.SizeOf(lpPipeAttributes);
        lpPipeAttributes.bInheritHandle = true;
        lpPipeAttributes.lpSecurityDescriptor = IntPtr.Zero;
        IntPtr attr = Marshal.AllocHGlobal(Marshal.SizeOf(lpPipeAttributes));
        Marshal.StructureToPtr(lpPipeAttributes, attr, true);
        SafeFileHandle hWritePipe = null;
        try
        {
            if (parentInputs)
                CreatePipeWithSecurityAttributes(out childHandle, out hWritePipe, attr, 0);
            else
                CreatePipeWithSecurityAttributes(out hWritePipe, out childHandle, attr, 0);
            if (!DuplicateHandle(GetCurrentProcess(), hWritePipe, GetCurrentProcess(), out parentHandle, 0, false, 2))
                throw new Exception();
        }
        finally
        {
            if ((hWritePipe != null) && !hWritePipe.IsInvalid)
            {
                hWritePipe.Close();
            }
        }
    }
    
    public static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
        IntPtr lpPipeAttributes, int nSize)
    {
        hReadPipe = null;
        if ((!CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize) || hReadPipe.IsInvalid) || hWritePipe.IsInvalid)
            throw new Exception();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接