不安全代码对安全代码有任何影响吗?
本文关键字:代码 影响 任何 安全 不安全 | 更新日期: 2023-09-27 17:50:12
据我所知,将方法标记为不安全将禁用该代码上的一些CLR检查,但这是否对安全的系统的其余部分有任何影响,除了DLL/EXE不能在不受信任的环境中运行的事实。
特别是
- 是否有任何安全检查不能在完整的dll上工作,因为它被标记为不安全?
- 如果一个DLL被标记为不安全,但标记为不安全的方法是没有实际调用,这是否与将DLL标记为安全吗?
- 将不安全代码保存在独立的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
呼叫。
此时,您可以看到副本的数量甚至不是问题。事实上,它被转换为引用类型(框)意味着我们最好把它作为一个引用类型开始。