最近發如今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中已經修復了這個問題,經過比較nfd
與buffers
數組的長度避免數組越界。相應代碼變成下面這樣。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]); }