关于Linux系统basename函数缺陷的思考

某模块作为前台进程独立运行时,运行命令携带命令行参数;作为某平台下守护进程子进程运行时,需要将命令行参数固化在代码里。类似如下写法:





char *argv[] = {"./DslDriver", "-t",
"/bin/VdslModemSco.bin"};

int argc = sizeof(argv) / sizeof(argv[0]);

随后,调用basename函数(头文件为libgen.h)解析argv[0],即"./DslDriver"。实测发现,在Linux原生系统中解析正常,在某平台下解析时则会发生段错误。

合理的想法自然是怀疑两种环境下basename函数的实现不同。Linux原生函数源码未找到,但某平台uclibc源码中可以找到basename函数的实现:

1 /* Return final component of PATH.
2 This is the weird XPG version of this function. It sometimes will
3 modify its argument. Therefore we normally use the GNU version (in
4 <string.h>) and only if this header is included make the XPG
5 version available under the real name. */
6 extern char *__xpg_basename (char *__path) __THROW;
7 #define basename __xpg_basename

Libgen.h

先不管注释内容,直接找到__xpg_basename定义处:

 1 char *__xpg_basename(register char *path)
2 {
3 static const char null_or_empty[] = ".";
4 char *first;
5 register char *last;
6
7 first = (char *) null_or_empty;
8
9 if (path && *path) {
10 first = path;
11 last = path - 1;
12
13 do {
14 if ((*path != ‘/‘) && (path > ++last)) {
15 last = first = path;
16 }
17 } while (*++path);
18
19 if (*first == ‘/‘) {
20 last = first;
21 }
22 last[1] = 0; //注意此句!
23 }
24
25 return first;
26 }

Wstring.c

可见该函数对path参数尾部增加了结束符(‘\0‘)。此刻真相大白,basename("./DslDriver")传入的是只读字符串,导致basename函数内试图修改只读数据区,当然会发生段错误。而运行命令中,命令行参数并非只读数据(实为字符数组),因此不会发生段错误。

若要在某平台下安全使用basename函数,有两种改法:

1) 传入字符数组。如下:




char argv[][sizeof("/bin/VdslModemSco.bin")] = {"./DslDriver", "-t",
"/bin/VdslModemSco.bin"};

2)
改用GNU的实现版本(头文件为string.h)。GNU版本绝不会更改它的参数,因此可正确处理静态字符串。

【思考】

回想一下,basename内有必要对path增加结束符吗?个人觉得没必要,这样反会限制函数的应用场景。理论上讲,若path为只读字符串,说明调用者确知path内容,手工剥离即可,无需调用basename函数(源码的“合理”之处)。只是本模块的使用方式较为特殊。另一方面,该缺陷也警醒编码者,不要"自作多情"地memset入参内容(使之失去累积性),或对入参字符串添加结束符。这些细节还是留待调用者自由发挥罢~

【Tips

如何查找库函数的原型定义所在处?

很简单,在调用处"改写"函数声明。例如,期望函数原型为int Func(char
*)——通常可由调用方式猜出,则在调用前声明为int Func(int)。编译器会通过conflicting types错误来指示previous
declaration of TheLibFunc

关于Linux系统basename函数缺陷的思考

时间: 2024-10-04 23:15:13

关于Linux系统basename函数缺陷的思考的相关文章

Unix/Linux系统时间函数API

首先说明关于几个时间的概念: 世界时:起初,国际上的标准时间是格林尼治标准时间,以太阳横穿本初子午线的时刻为标准时间正午12点.它根据天文环境来定义,就像古代人们根据日晷来计时一样,如下图: 原子时:地球一年中自转的速度并不是恒定的,它有时候转的快,有时候转的慢,地震可能使得地球自转加快,而发射卫星则使得自转减缓,但地球的总体自转趋势是越来越慢.如果按照地球自转一圈固定为24小时来定义时间长度,会导致一秒钟的长度不稳定,或长或短.为解决这个问题,科研人员发现某元素的原子运动频率很稳定,可以以该元

Linux系统时间函数

先来说说自己在做工程过程中的一些理解: 1, 输入time_t,输出tm格式的函数 loctaltime(time_t) / gmtime(time_t) 其中localtime会受时区和夏令时影响,也就是说系统会把函数输入的time_t格式的值作为UTC时间,然后根据本地的TZ环境变量,进行小时的偏移得到一个tm格式的时间: gmtime则不做环境变量相关的处理,直接获得tm格式的值. 2, 输入tm,输出time_t格式的函数mktime() 这个函数也受TZ环境变量的影响,确切来说受夏令时

这里给大家整理了一些Linux系统运维相关的面试题,有些问题没有标准答案,希望要去参加Linux运维面试的朋友,可以先思考下这些问题

这里给大家整理了一些Linux系统运维相关的面试题,有些问题没有标准答案,希望要去参加Linux运维面试的朋友,可以先思考下这些问题. 一.Linux操作系统知识 1.常见的Linux发行版本都有什么?你最擅长哪一个?它的官网网站是什么?说明你擅长哪一块? 2.Linux开机启动流程详细步骤是什么?系统安装完,忘记密码如何破解? 3.企业中Linux服务器系统分区标准是什么?(以硬盘为300G,内存16G为例) 4.某一天突然发现Linux系统文件只读,该怎么办呢?完整操作步骤. 5.安装一台系

Linux系统运维面试题,Linux运维经典面试题讲解

这里给大家整理了一些Linux系统运维相关的面试题,有些问题没有标准答案,希望要去参加Linux运维面试的朋友,可以先思考下这些问题. 1.Linux如何挂载windows下的共享目录? mount.cifs //IP地址/server /mnt/server -o user=administrator,password=123456 linux 下的server需要自己手动建一个 后面的user与pass 是windows主机的账号和密码 注意空格 和逗号 2.如何查看http的并发请求数与其

linux 系统函数之 (dirname, basename)【转】

转自:http://blog.csdn.net/peter_cloud/article/details/9308333 版权声明:本文为博主原创文章,未经博主允许不得转载. 除非你的原件考虑跨平台. 在Linux编程多使用一些系统函数真的很方便,哎没办法越来越懒~~~~~~ 今天记录一下dirname 和basename这两个简单的处理文件路径的linux系统函数. 头文件: #include <libgen.h> 函数定义: char *dirname(char *path); char *

linux 系统函数 basename和dirname

在linux系统中有这样两个系统函数,basename 和  dirname 1.basename 用于 获取文件名, 1.1 当给定扩展名作为参数之后,甚至可以直接获取文件名 2.与basename 相对应的 dirname 就是获取文件的路径,哈哈: linux 真是一个神奇的的系统. 原文地址:https://www.cnblogs.com/SliverLee/p/11441140.html

Linux系统编程-setitimer函数

功能:linux系统编程中,setitimer是一个经常被使用的函数,可用来实现延时和定时的功能. 头文件:sys/time.h 函数原型: int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); 参数含义: 1.which参数用来设置定时器类型,可选的值为 (1)ITIMER_REAL : 设置定时器以系统真实所花费的时间来计时,运行指定时间后发送SIGALRM信号. (

exit()与_exit()函数的区别(Linux系统中)

注:exit()就是退出,传入的参数是程序退出时的状态码,0表示正常退出,其他表示非正常退出,一般都用-1或者1,标准C里有EXIT_SUCCESS和EXIT_FAILURE两个宏,用exit(EXIT_SUCCESS);可读性比较好一点. 作为系统调用而言,_exit和exit是一对孪生兄弟,它们究竟相似到什么程度,我们可以从Linux的源码中找到答案: #define __NR__exit __NR_exit /* 摘自文件include/asm-i386/unistd.h第334行 */

嵌入式 Linux系统编程(五)——目录文件函数

嵌入式 Linux系统编程(五)--目录文件函数 Linux中目录也是文件,目录操作函数为标准IO库函数.主要函数如下: #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); DIR *fdopendir(int fd); 成功返回一个指向目录流的指针,失败返回NULL,并且设置errno全局变量. #include <dirent.h> struct dirent *rea