不安全的代码对安全代码有影响吗?

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

特别是,

  1. 它们是否是对完整dll无效的任何安全检查,因为它被标记为不安全?
  2. 如果DLL被标记为不安全,但标记为不安全的方法实际上没有被调用,这是否与DLL标记为安全相同?
  3. 将不安全的代码保存在单独的DLL中是否有任何运行时的好处?

我有重写64位窗口上的嵌套控件的问题,如此处详述和解决方案(看起来工作的那个)涉及不安全的代码,我想了解添加此代码对我的项目的影响。

您的问题的答案是: unsafe关键字并不意味着“不安全”,它意味着“可能不安全”。 编译器和框架无法确保它是安全的。 您可以确保代码无法对内存执行不安全的读取或写入操作。

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

1)重新设计应用程序以减少容器 数量减少嵌套级别

如果您使用容器仅用于控制安排,请编写您自己的容器,可以使用一个级别进行所有安排。

更新

您可以修改该文章中的代码,使其不使用指针(即不需要unsafe关键字)。 请记住,现在需要编组,这意味着额外的复制。 这可能是一件好事,因为原始代码将一个WINDOWPOS指针从OS传递给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。 编写托管方法,如果您的目的是公开所提供的function,则调用私有p / invoke声明; 在这种情况下不是真的,p / invoke是严格内部的。

(*)好的,一个例外。 您正在创建NativeMethodsUnsafeNativeMethods等。这是FxCop进行p / invoke的推荐方法。

更新

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

如果WINDOWPOS是值类型,那么这将是事件序列:

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

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

2)第二份?

 BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos); 

等待! BeginInvoke的签名是(Delegate, params object[]) 。 这意味着wpos将被装箱。 所以是的,第二个副本发生在这里:拳击操作。

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

3)对ResizeChild调用进行Unbox和复制。

此时您可以看到副本数量甚至不是问题。 它被转换为引用类型(盒装)这一事实意味着我们最好将其作为引用类型开始。

不安全的代码能够 破坏托管堆 。 因此,在同一过程中运行的任何事物都会受到影响。

这包括同一进程中的所有其他以及可能的所有其他AppDomain


UPDATE

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


更新2

是不是很难写的不安全的代码?

没有。在.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; }