freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

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

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客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录