C语言开发不透明数据类型库 如何防范用户修改内部数据
核心结论
这类无限制的内存操作是C语言的固有特性,不存在100%阻止用户修改不透明类型内部数据的手段,能写出合法C代码的程序员想绕过防护总有办法。 但我们可以通过多种手段大幅提高用户误操作/恶意操作的门槛,尽可能降低这类问题出现的概率。
可行的防护方案
方案1:用句柄替代直接指针返回
不要对外暴露struct lew_arr*类型的指针,改为返回整数类型的自定义句柄,库内部维护全局(或线程安全的)句柄映射表,存储句柄和真实结构体实例的对应关系。用户拿到的只有整数句柄,完全没有直接操作结构体内存的入口,从根源上避免指针强制转换的问题。
这种方案的缺点是会增加少量的性能开销,同时需要处理句柄的分配、回收以及线程安全问题。方案2:增加编译期检查约束
借助编译器扩展能力增加非法操作的编译警告:- 给对外暴露的函数指针参数添加编译器属性,比如GCC/Clang的
__attribute__((access)),限制指针的访问范围 - 文档中明确标注不透明类型指针禁止强制转换为其他类型访问,同时在公共头文件中添加注释警告
本身不透明类型就已经无法在库外执行sizeof(struct lew_arr)、直接访问成员等操作,大部分正常开发的用户看到不透明类型就不会主动去做强制转换的操作。
- 给对外暴露的函数指针参数添加编译器属性,比如GCC/Clang的
方案3:运行期完整性校验
在结构体的前后添加金丝雀(Canary)校验字段:
库内部每次操作结构体前都先校验头尾的金丝雀值是否和初始化时一致,如果不一致直接抛出错误终止运行。用户乱改内存大概率会破坏金丝雀值,能第一时间暴露问题,避免出现隐蔽的内存污染bug。struct lew_arr { uint32_t canary_head; // 头部校验值,初始化时写入固定或随机的魔数 void *buff; size_t len; // 数组元素数量 size_t cap; // 数组容量 size_t sz; // 单元素字节数 uint32_t canary_tail; // 尾部校验值 };
最终建议
正常使用你库的程序员不会闲的没事去强制转换不透明类型的指针改内存,会这么做的要么是本身水平不够不知道自己在干什么,要么就是故意搞破坏。前者你可以通过完善的文档、入门示例引导正确使用,后者不管加什么防护都防不住,符合C语言的设计哲学:信任程序员知道自己在做什么,给程序员足够的操作自由,同时也要求程序员为自己的操作负责。
内容的提问来源于stack exchange,提问作者Drlew
相关产品推荐
相关产品推荐

