进程环境变量:putenv函数的不安全问题

本文通过分析两个demo演示了putenv和setenv在设置环境变量时的区别。putenv可能导致浅拷贝问题,当函数作用域结束时,由于栈帧释放,环境变量可能变得不可用,甚至引发异常。而setenv则会复制变量内容,避免此类问题。建议在关心内存安全的情况下使用setenv代替putenv。
摘要由CSDN通过智能技术生成

前言

在做项目的时候发现一个bug,调了2小时多最后发现,于是写了两个对应的demo来解释getenv的坑。

一般来说我们可以通过环境变量来达到传参的目的。而环境传参可以比管道传参更加快捷和方便。因为进程地址空间中在栈区和内核空间之间有一层命令行参数和环境变量,因此可以获悉fork()的时候会拷贝一份,exec*()的时候只填充代码区和数据区,不改变之前创建的内核相关的数据结构,其中就包括进程地址空间mm_struct,因此环境变量具有全局属性可以用来传参。

常见的设置环境变量的方法有putenv和setenv。下文讲述的是putenv方法的浅拷贝问题。

现象1

在这份demo1中我们可以看到环境变量正确地被读出。
在这里插入图片描述
在这份demo2中我们看到环境变量只出现了一半。
在这里插入图片描述

现象2

基于上面的现象,有理由怀疑可能是指针指向的是栈上的字符串,函数结束的时候栈帧释放内存未清,导致出现部分环境变量。我们可以在函数结束的末尾再加入一些申请栈上空间的行为。

在demo1中,一切正常
在这里插入图片描述
而此时demo2已经抛异常了。
在这里插入图片描述

gdb查看

在崩溃的地方使用bp查看调用堆栈
在这里插入图片描述

在这里插入图片描述

上边都是库函数,不考虑,看看b.cc第九行是什么代码

在这里插入图片描述
看来就是getenv返回NULL了,然后用NULL去实例化data。

验证

我们尝试如此打印看看是否真如我们开始猜测的那样,是在函数结束后出的问题。
在这里插入图片描述
可以看到demo1始终没问题。
在这里插入图片描述

而demo2在函数栈帧中可以对应输出环境变量,等出了栈帧就报错了。
在这里插入图片描述

分析

putenv存的是char*指针,然后出了作用域string调了析构。而如果char*指的是堆上的,出去后直接就访问野指针了。这里还能打印的原因是在各个编译器实际实现string中,16以内的字符是实现在栈上的并不是存在堆区的。所以出来后char*其实指着栈上的字符串,虽然栈帧已经被收回,对应的内存内容没有清空。所以继续在栈上开变量之后就崩了。

这也是别人博客说明过的:

  • putenv函数:
    putenv 函数会将参数 string 直接填写到环境表中,不会再为 “name=value” 这个字符串再去分配内存。如果是在一个函数中定义的string,那么在调用该函数后,string 指向的内容可能会被释放,就找不到name环境变量的值了。
  • setenv 函数:
    setenv 函数和 putenv 函数不同,它会将name和value指向的内容复制一份并为其分配内存,形成 “name=value” 的字符串,并将其地址写入到环境表中。所以就不会出现上面putenv 的情况,就算函数返回了,name 和 value指向的内容被释放了,仍然有一份拷贝在

结论

putenv本身是浅拷贝的,不安全,如果担心问题可以使用setenv。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值