freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

open函数与条件竞争
2021-11-15 21:45:56

上次简单的介绍了一下基础的IO函数,但是当时也说过,那些代码都是有漏洞的。我们就来分析一下漏洞以及修复的方式

条件竞争

还拿上次的copy小工具来举例

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,char **argv){
	if(argc<3){
		printf("input error");
		exit(1);
	}
	char buf;
	int fd = open(argv[1],O_RDONLY);
	if(fd == -1){
		perror("open() fail");
		exit(1);
	}
	int fp = open(argv[2],O_WRONLY|O_CREAT,0666);
	if(fp == -1){
		perror("open() fail");
		exit(1);
	}
	for(;read(fd,&buf,1);){
		
				
		write(fp,&buf,1);

	}

	close(fp);
	close(fd);
}

我们的工具是打开两个文件,然后将一个文件的内容放到另外一个文件里面去。重点关注一下fp这个文件。这个文件是存在则覆盖,不存在则创建。我们都知道如果open打开一个文件的时候其文件位置指针就会放到文件的最前面。那么如果里面本来就有内容那么就会被覆盖。

我们试想一下这样的情况,当我们同时启动这样的两个进程,然后同时都往同一个文件里面传输数据,会怎么样?进程调度的过程中一个进程没有执行结束,但是它的时间片已经结束了,于是换成了另外一个进程。该进程又打开了文件,将里面的数据覆盖掉。

我们来看一个简单一些的代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,char **argv){
	int fd = open(argv[1],O_WRONLY);
	if(fd!=-1){
		printf("The file has exists already!");
		close(fd);
	}
	else{
		fd = open(argv[1],O_WRONLY|O_CREAT,0666);
		if(fd == -1){
				perror("open()");
				exit(1);
		}
		printf("The file creat successfully");
		}
}

该进程要创建一个文件,而且必须要是该进程本身创建的文件。那么这样可以保证文件一定是自己的原创了吗?并不一定,我们开启两个相同的该进程,在进程A

if(fd!=-1){
		printf("The file has exists already!");
		close(fd);
}

这条语句结束以后,进程A的时间片用完了,然后进程B开始执行,同样的文件名。然后B成功创建,但是A该执行else语句了,所以A也以为这个文件是它自己创建的。我们对代码进行一个小小的修改

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,char **argv){
	int fd = open(argv[1],O_WRONLY);
	if(fd!=-1){
		printf("The file has exists already!");
		close(fd);
	}
	else{
    if(argc > 2)
        sleep(10);
		fd = open(argv[1],O_WRONLY|O_CREAT,0666);
		if(fd == -1){
				perror("open()");
				exit(1);
		}
		printf("The file creat successfully");
		}
}

我们专门设置一个这样的语句,要求睡眠的进程我们输入三个参数,多加一个sleep。这就是我们所说的进程A。然后正常执行的进程我们输入参数是只输入两个参数
image.png
专门开了两个shell,我们可以看一下两个进程都认为文件是它们创建的。两个进程同时对同一个资源进行访问,最后造成了错误。我们就将这种情况称为条件竞争。我们看一下进程执行的流程
image.png
考虑一个更糟糕的情况,比如说需要往里面写数据,那么就会产生覆盖的情况

原子操作

出现上述情况我们就要想办法将其避免。上面的情况主要是因为进程执行的时候指令被中断掉了,那么我们就要依靠原子操作。原子操作是什么?原子操作就是说一个指令要么不执行,只要执行就执行完。在并发执行中经常用到。

上述代码中我们就要添加原子操作,比如这样fd = open(argv[1],O_WRONLY|O_CREAT|O_EXCL,0666);。当O_CREAT标志和O_EXCL标志同时存在时,就会进行检测。如果文件存在则会发生报错。这样的语句是不能被打断的,一旦执行就要执行到底。也通过这类方式避免了open类型的条件竞争。

读取和更改文件的标志位

F_GETFL参数

除了以上述方式更改,还有一种方法。通过调用fcntl函数来进行设置。所以这里就先简单介绍一下这个函数
image.png
其中fd表示的是传入的文件描述符,cmd表示指令。一共有很多不同的指令,这里我们先介绍F_GETFL和F_SETFL指令。然后第三个参数在某些情况下需要用到。首先来看一下F_GETFL。我们先来简单的用一下这个函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
	int fd = open("obj_getfl",O_WRONLY|O_CREAT,0666);
	if(fd == -1){
		perror("open()");
		exit(1);

	}
	int flag = fcntl(fd,F_GETFL);
	if(flag == 1){
		perror("fcntl()");
		exit(1);

	}
	printf("the flag of the process %d is %d",getpid(),flag);
	gets();
	return 0;
 }

然后我们看一下输出的内容
image.png
最后返回的是文件的状态标志image.png
根据这个标志,我们可以判断一个文件是否具有某个状态。我们只需要让flag和一个状态标志位进行按位与运算就可以得出。

我们还可以判断一个文件的访问情况。是以什么权限进行的访问,可读、可写还是读写。但是访问权限并不与打开文件的状态标志位的单个比特位对应,所以还需要掩码O_ACCMODE参与运算,这里还有一个公式
accessMode = flag & O_ACCMODE
最终得到accessMode以后才可以得出访问状态。

我们再把open的标志位复习一下
image.png
image.png

其中最上面的三个是文件的访问模式,中间的这部分是文件的创建标志,最后的五个是已经打开文件的状态标志

然后我们返回去看上面的输出32769,然后我们去往这个目录:/proc/11128/fdinfo,然后使用ls查看打开文件
image.png
其中0、1、2就不再多说,我们使用cat 3
image.png
其中有个flags字段,该字段记录了打开文件的状态标志。以0开头表示这是8进制,然后变为十进制以后就会发现正是我们的flag的值。那么O_ACCMODE又是什么呢?该值是八进制中的3。也就是二进制中的11,我们的访问模式需要两个标志位来表示。00代表只读,01代表只写,11代表读写。这也就是为什么我们需要依靠O_ACCMODE来得到文件访问标志了。

F_SETFL参数

说完得到flag值,然后我们就可以讲解一下修改falgs的值了。第二个参数中填写F_SETFL,第三个参数中填写flag。通过这种方式我们可以更改一些flags标志
flags |= O_APPEND;fcntl(fd,F_SETFL,flags);

这就是有关open的条件竞争和解决条件竞争的方法,也就是通过原子操作来使指令不可被打断。我们还可以通过fcntl函数来修改文件的标志,通过修改标志来防止条件竞争

# linux # 系统编程 # c语言 # 条件竞争
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录