非托管函数指针返回时触发SIGSEGV(段错误)问题排查
解决C#调用OpenSSL回调时的SIGSEGV段错误问题
看起来你的问题核心出在委托签名不匹配,加上可能存在的委托被GC回收的隐患,导致回调返回时栈损坏或者访问无效内存,触发段错误。我们一步步拆解问题并修复:
1. 核心问题:委托签名与C函数不匹配
原C语言的回调函数签名是:
int (*cb) (SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)
而你最初定义的C#委托是:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int VerifyCookieDelegate(IntPtr ssl, [MarshalAs(UnmanagedType.LPStr)] string cookie, int cookie_len);
这里的关键错误是:
const unsigned char *cookie是二进制缓冲区,不是以\0结尾的字符串,你用[MarshalAs(UnmanagedType.LPStr)] string会让.NET自动尝试从指针读取到null终止符,这会直接越界读取cookie_len之外的内存,破坏进程空间,最终在回调返回时触发SIGSEGV。
2. 修复步骤
第一步:修正委托签名
把cookie参数改为IntPtr,完全匹配C语言的指针类型,手动处理二进制数据:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int VerifyCookieDelegate(IntPtr ssl, IntPtr cookie, int cookie_len);
这样.NET不会自动做字符串marshal,避免越界读取的问题。
第二步:确保委托实例不被GC回收
如果你只是临时创建委托并传递给原生函数,.NET的GC可能会在回调被调用前回收这个委托实例,导致原生代码调用无效的函数指针,同样会触发段错误。需要把委托实例保存到一个长期存在的变量中,比如类的成员:
// 类成员变量,保存委托实例防止GC回收 private VerifyCookieDelegate _persistedVerifyCookieDelegate; // 设置回调的代码 public void SetCookieVerifyCallback(IntPtr ctxPtr) { _persistedVerifyCookieDelegate = VerifyCookieThunk; SSL_CTX_set_cookie_verify_cb(ctxPtr, _persistedVerifyCookieDelegate); }
第三步:确认回调实现的细节
你的VerifyCookieThunk实现已经用IntPtr处理cookie,这部分是正确的,但要注意:
- 确保
Marshal.Copy的参数没有越界(你当前的代码是对的) - 确认返回值和C代码的约定一致:C代码中返回1表示验证通过,0表示失败,你的返回逻辑要对应(比如
Native.FAIL的值要和C代码的失败返回值匹配)
3. 验证修复后的完整代码示例
private const string SSLDLLNAME = "libssl.so"; // 或者对应Windows的libssl.dll [DllImport(SSLDLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern void SSL_CTX_set_cookie_verify_cb(IntPtr ctx, VerifyCookieDelegate callback); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int VerifyCookieDelegate(IntPtr ssl, IntPtr cookie, int cookie_len); // 保存委托实例,防止GC回收 private VerifyCookieDelegate _persistedVerifyCookieDelegate; private Func<Ssl, byte[], bool> _verifyCookieCallback; public void InitializeVerifyCallback(IntPtr ctxPtr, Func<Ssl, byte[], bool> verifyCallback) { _verifyCookieCallback = verifyCallback; _persistedVerifyCookieDelegate = VerifyCookieThunk; SSL_CTX_set_cookie_verify_cb(ctxPtr, _persistedVerifyCookieDelegate); } public int VerifyCookieThunk(IntPtr sslPtr, IntPtr cookie, int cookieLength) { if (!TryGetInstance(sslPtr, out Ssl ssl)) return Native.FAIL; var cookieBytes = new byte[cookieLength]; Marshal.Copy(cookie, cookieBytes, 0, cookieLength); return _verifyCookieCallback(ssl, cookieBytes) ? 1 : 0; }
这样修改后,委托签名完全匹配C函数,且委托实例不会被GC回收,应该就能解决回调返回时的SIGSEGV问题了。
内容的提问来源于stack exchange,提问作者Matthew Goulart
相关产品推荐
相关产品推荐

