C#调用libnl的nla_parse时struct nlattr双指针的正确封送问题
核心错误点
原生nla_parse的第一个参数struct nlattr **tb是nlattr类型的指针数组,数组中每个元素存储的是指向实际nlattr结构体的指针,而不是存储nlattr结构体本身的数组,你当前的申请逻辑完全不符合预期:
- 你申请的内存大小为
sizeof(nlattr) * (NL80211_ATTR_MAX +1),每个元素仅占用4字节(两个ushort的总长度) - 实际需要的内存大小为
IntPtr.Size * (NL80211_ATTR_MAX +1),32位系统下每个元素占4字节、64位系统下每个元素占8字节,64位场景下你申请的内存只有实际需要的一半,nla_parse写入时就会越界破坏堆内存,触发malloc(): memory corruption报错。
修复方案
方案1(推荐,不需要手动管理非托管内存)
直接修改nla_parse的extern签名,让.NET运行时自动处理指针数组的封送:
// 修正后的extern签名,[Out]特性指示运行时把原生写入的数组内容封送回托管层 public static extern int nla_parse([Out] IntPtr[] tb, int maxtype, IntPtr head, int len, IntPtr policy);
调用逻辑简化为:
var NL80211_ATTR_MAX = 305; // 直接声明托管的指针数组即可 IntPtr[] tb = new IntPtr[NL80211_ATTR_MAX + 1]; var returnCode = NetlinkDemo.nla_parse(tb, NL80211_ATTR_MAX, gnlMsgAttrDataHeaderPtr, gnlMsgAttrLength, IntPtr.Zero); // 后续读取属性示例 int targetAttrType = 10; // 替换为你要读取的属性类型 if (tb[targetAttrType] != IntPtr.Zero) { nlattr attr = Marshal.PtrToStructure<nlattr>(tb[targetAttrType]); // 处理属性逻辑 }
方案2(保留手动申请非托管内存的写法)
修改内存申请的大小计算逻辑:
var NL80211_ATTR_MAX = 305; // 按指针长度计算数组总大小 var nlAttrPtrArraySize = IntPtr.Size * (NL80211_ATTR_MAX + 1); var nlAttrPtrArrayPtr = Marshal.AllocHGlobal(nlAttrPtrArraySize); // 调用nla_parse var returnCode = NetlinkDemo.nla_parse(nlAttrPtrArrayPtr, NL80211_ATTR_MAX, gnlMsgAttrDataHeaderPtr, gnlMsgAttrLength, IntPtr.Zero); // 后续读取属性示例 int targetAttrType = 10; // 按指针偏移读取对应位置的指针值 IntPtr attrPtr = Marshal.ReadIntPtr(nlAttrPtrArrayPtr, targetAttrType * IntPtr.Size); if (attrPtr != IntPtr.Zero) { nlattr attr = Marshal.PtrToStructure<nlattr>(attrPtr); // 处理属性逻辑 } // 一定要记得释放手动申请的非托管内存,避免泄漏 Marshal.FreeHGlobal(nlAttrPtrArrayPtr);
额外检查项
如果修改后仍有问题,再排查两个点:
- 确认
gnlMsgAttrLength是通过genlmsg_attrlen接口计算得到的,不要手动硬算长度,长度错误也会导致nla_parse越界访问 - 确认你用到的
nlmsghdr、genlmsghdr等其他Netlink相关结构体的布局、字段顺序、字段类型和原生C定义完全一致,结构体字段映射错误也会导致指针计算偏移错误
内容的提问来源于stack exchange,提问作者Veverke
相关产品推荐
相关产品推荐

