bash執行shell腳本時報錯 save_bash_input: buffer already exists for new fd XXX

最近發如今lua中經過os.execute執行系統shell腳本時,偶爾會發生錯誤退出,最後發現是bash自己的問題。shell

對於下面的shell腳本數組

#!/bin/bash

for fd in `seq 11 254`; do
    eval "exec $fd< data.txt"
done

for fd in `seq 256 274`; do
    eval "exec $fd< data.txt"
done

eval "exec 255>&-"
echo "done"

bash執行時會發生段錯誤bash

$ touch data.txt
$ bash test.sh
test.sh: line 11: save_bash_input: buffer already exists for new fd 275
Segmentation fault (core dumped)

bash執行shell腳本時,默認使用255的文件描述符打開/讀取當前執行的這個shell腳本。這裏將11-254,256-274這些文件描述符都使用了,而後調用exec 255>&-操做能夠關閉表明當前shell腳本的文件描述符。這時便會發生錯誤。this

看了下bash的實現,原來bash中操做的文件描述符,bash會爲其建立一個buffer緩衝區,放在一個數組buffers中。在shell腳本中調用exec關閉文件描述符時, 若是要關閉的文件描述符剛好指向當前執行的shell腳本時,bash會複製出一個新的文件描述符,將舊的關閉,以後用新的文件描述符讀取shell腳本。對於這個新的文件描述符若是過大的話,會超過buffers數組的大小,致使內存訪問越界。lua

bash的實現代碼以下所示code

int
save_bash_input (fd, new_fd)
     int fd, new_fd;
{
  int nfd;

  /* Sync the stream so we can re-read from the new file descriptor.  We
     might be able to avoid this by copying the buffered stream verbatim
     to the new file descriptor. */
  if (buffers[fd])
    sync_buffered_stream (fd);

  /* Now take care of duplicating the file descriptor that bash is
     using for input, so we can reinitialize it later. */
  nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
  if (nfd == -1)
    {
      if (fcntl (fd, F_GETFD, 0) == 0)
	sys_error (_("cannot allocate new file descriptor for bash input from fd %d"), fd);
      return -1;
    }

  if (buffers[nfd])
    {
      /* What's this?  A stray buffer without an associated open file
	 descriptor?  Free up the buffer and report the error. */
      internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
      free_buffered_stream (buffers[nfd]);
    }

buffers是一個buffer數組,數組的索引是文件描述符,值爲對應的緩衝區。這裏的nfd是複製產生的新的文件描述符,buffers[nfd]這裏沒有判斷buffers數組的長度,一旦nfd過大,就會發生數組越界訪問。索引

貌似bash 4.4中已經修復了這個問題,經過比較nfdbuffers數組的長度避免數組越界。相應代碼變成下面這樣。ip

if (nfd < nbuffers && buffers[nfd])
    {
      /* What's this?  A stray buffer without an associated open file
	 descriptor?  Free up the buffer and report the error. */
      internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
      free_buffered_stream (buffers[nfd]);
    }
相關文章
相關標籤/搜索