不安全代码对安全代码有任何影响吗?

本文关键字:代码 影响 任何 安全 不安全 | 更新日期: 2023-09-27 17:50:12

据我所知,将方法标记为不安全将禁用该代码上的一些CLR检查,但这是否对安全的系统的其余部分有任何影响,除了DLL/EXE不能在不受信任的环境中运行的事实。

特别是

  1. 是否有任何安全检查不能在完整的dll上工作,因为它被标记为不安全?
  2. 如果一个DLL被标记为不安全,但标记为不安全的方法是没有实际调用,这是否与将DLL标记为安全吗?
  3. 将不安全代码保存在独立的DLL ?

我有在64位窗口上重新绘制嵌套控件的问题,详细说明在这里和一个解决方案(似乎工作的)涉及不安全的代码,我想了解添加此代码对我的项目的影响。

不安全代码对安全代码有任何影响吗?

不安全的代码能够破坏托管堆。因此,在同一进程中运行的任何内容都可能受到影响。

这包括所有其他和可能在同一进程中的所有其他 appdomain


<标题> 更新

下面是一个例子:http://blogs.msdn.com/b/tess/archive/2006/02/09/net-crash-managed-heap-corruption-calling-unmanaged-code.aspx


<标题>更新2 h1> p>是编写的不安全代码努力坏?

。在。net框架本身中有大量的不安全代码。例子很多,但这里有一个在System.String:

public static unsafe string Copy(string str)
{
    if (str == null)
    {
        throw new ArgumentNullException("str");
    }
    int length = str.Length;
    string str2 = FastAllocateString(length);
    fixed (char* chRef = &str2.m_firstChar)
    {
        fixed (char* chRef2 = &str.m_firstChar)
        {
            wstrcpyPtrAligned(chRef, chRef2, length);
        }
    }
    return str2;
}

你的问题的答案是:unsafe关键字并不意味着"不安全",它意味着"潜在的不安全"。编译器和框架无法确保它是安全的。由您来确定代码不能对内存执行不安全的读取或写入。

我强烈建议你遵循你所链接的文章中给出的建议:

1)重新设计应用程序,使拥有更少的容器减少嵌套级别的数量

如果你使用容器的唯一目的是安排控制,写你自己的容器,可以在一个层次上完成所有的安排。

您可以修改那篇文章中的代码,使其不使用指针(即不需要不安全关键字)。请记住,这现在需要封送,这意味着额外的复制。这可能是一件好事,因为原始代码正在将WINDOWPOS指针从操作系统传递给BeginInvoke,而该指针在操作系统生成指针的同一调度事件期间不会执行。换句话说,这段代码已经很臭了。

internal class MyTabPage : TabPage
{
    private const int WM_WINDOWPOSCHANGING = 70;
    private const int WM_SETREDRAW = 0xB;
    private const int SWP_NOACTIVATE = 0x0010;
    private const int SWP_NOZORDER = 0x0004;
    private const int SWP_NOSIZE = 0x0001;
    private const int SWP_NOMOVE = 0x0002;
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    extern static int SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);
    [DllImport("User32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    extern static bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter,
    int x, int y, int cx, int cy, int flags);
    [StructLayout(LayoutKind.Sequential)]
    private class WINDOWPOS
    {
        public IntPtr hwnd;
        public IntPtr hwndInsertAfter;
        public int x;
        public int y;
        public int cx;
        public int cy;
        public int flags;
    };
    private delegate void ResizeChildDelegate(WINDOWPOS wpos);
    private void ResizeChild(WINDOWPOS wpos)
    {
        // verify if it's the right instance of MyPanel if needed
        if ((this.Controls.Count == 1) && (this.Controls[0] is Panel))
        {
            Panel child = this.Controls[0] as Panel;
            // stop window redraw to avoid flicker
            SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 0, 0);
            // start a new stack of SetWindowPos calls
            SetWindowPos(new HandleRef(child, child.Handle), new HandleRef(null, IntPtr.Zero),
            0, 0, wpos.cx, wpos.cy, SWP_NOACTIVATE | SWP_NOZORDER);
            // turn window repainting back on 
            SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 1, 0);
            // send repaint message to this control and its children
            this.Invalidate(true);
        }
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_WINDOWPOSCHANGING)
        {
            WINDOWPOS wpos = new WINDOWPOS();
            Marshal.PtrToStructure(m.LParam, wpos);
            Debug.WriteLine("WM_WINDOWPOSCHANGING received by " + this.Name + " flags " + wpos.flags);
            if (((wpos.flags & (SWP_NOZORDER | SWP_NOACTIVATE)) == (SWP_NOZORDER | SWP_NOACTIVATE)) &&
            ((wpos.flags & ~(SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE)) == 0))
            {
                if ((wpos.cx != this.Width) || (wpos.cy != this.Height))
                {
                    BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos);
                    return;
                }
            }
        }
        base.WndProc(ref m);
    }
}

注释: WINDOWPOS从值类型到引用类型的改变是有意的。使用引用类型将副本数量减少到只有一个(初始封送)(**)。

再次更新

我只是注意到代码最初是将p/invoke声明公开的。永远不要在类(*)之外公开p/invoke。如果您的意图是公开所提供的功能,则编写调用私有p/invoke声明的托管方法;在这种情况下,p/invoke是严格的内部调用。

(*)好,有一个例外。你正在创建NativeMethods, UnsafeNativeMethods等。这是FxCop推荐的执行p/invoke的方法。

(**)我被要求(在其他地方)准确地描述为什么在这里使用引用类型更好,所以我在这里添加了这个信息。我被问到的问题是,"这不会增加记忆压力吗?"

如果WINDOWPOS是一个值类型,这将是事件的顺序:

从非托管内存复制到托管内存

WINDOWPOS wpos = Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));

2)第二份副本?

BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos);

等等!BeginInvoke的签名是(Delegate, params object[])。这意味着wpos将会被包围。所以,是的,这里发生了第二次复制:

BeginInvoke将委托和对象[]添加到调用列表中,并发布注册窗口消息。当消息泵将该消息从队列中移除时,将使用object[]参数调用委托。

3)打开盒子,复制ResizeChild呼叫。

此时,您可以看到副本的数量甚至不是问题。事实上,它被转换为引用类型(框)意味着我们最好把它作为一个引用类型开始。