opnsense-src/usr.bin/tail/tests/tail_test.sh
Dag-Erling Smørgrav 7441705cae tail: Fix heap overflow in -F case.
The number of events we track can vary over time, but we only allocate
enough space for the exact number of events we are tracking when we
first begin, resulting in a trivially reproducable heap overflow.  Fix
this by allocating enough space for the greatest possible number of
events (two per file) and clean up the code a bit.

Also add a test case which triggers the aforementioned heap overflow,
although we don't currently have a way to detect it.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	allanjude, markj
Differential Revision:	https://reviews.freebsd.org/D42839

(cherry picked from commit 621f45532c5887c96b708ce232c52878d0053325)

tail: Clean up error messages.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	markj
Differential Revision:	https://reviews.freebsd.org/D42842

(cherry picked from commit b70e57be2cfe83ec9f410e2f317ea38aaac61a98)
2023-12-13 17:41:48 +01:00

451 lines
11 KiB
Bash
Executable file

# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2016 Alan Somers
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
atf_test_case empty_r
empty_r_head()
{
atf_set "descr" "Reverse an empty file"
}
empty_r_body()
{
touch infile expectfile
tail -r infile > outfile
tail -r < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case file_r
file_r_head()
{
atf_set "descr" "Reverse a file"
}
file_r_body()
{
cat > infile <<HERE
This is the first line
This is the second line
This is the third line
HERE
cat > expectfile << HERE
This is the third line
This is the second line
This is the first line
HERE
tail -r infile > outfile
tail -r < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case file_rn2
file_rn2_head()
{
atf_set "descr" "Reverse the last two lines of a file"
}
file_rn2_body()
{
cat > infile <<HERE
This is the first line
This is the second line
This is the third line
HERE
cat > expectfile << HERE
This is the third line
This is the second line
HERE
tail -rn2 infile > outfile
tail -rn2 < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
# Regression test for PR 222671
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222671
atf_test_case pipe_leading_newline_r
pipe_leading_newline_r_head()
{
atf_set "descr" "Reverse a pipe whose first character is a newline"
}
pipe_leading_newline_r_body()
{
cat > expectfile << HERE
3
2
1
HERE
printf '\n1\n2\n3\n' | tail -r > outfile
printf '\n1\n2\n3\n' | tail -r > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case file_rc28
file_rc28_head()
{
atf_set "descr" "Reverse a file and display the last 28 characters"
}
file_rc28_body()
{
cat > infile <<HERE
This is the first line
This is the second line
This is the third line
HERE
cat > expectfile << HERE
This is the third line
line
HERE
tail -rc28 infile > outfile
tail -rc28 < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case file_rc28
file_rc28_head()
{
atf_set "descr" "Reverse a file and display the last 28 characters"
}
file_rc28_body()
{
cat > infile <<HERE
This is the first line
This is the second line
This is the third line
HERE
cat > expectfile << HERE
This is the third line
line
HERE
tail -rc28 infile > outfile
tail -rc28 < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case longfile_r
longfile_r_head()
{
atf_set "descr" "Reverse a long file"
}
longfile_r_body()
{
jot -w "%0511d" 1030 0 > infile
jot -w "%0511d" 1030 1029 0 -1 > expectfile
tail -r infile > outfile
tail -r < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case longfile_r_enomem
longfile_r_enomem_head()
{
atf_set "descr" "Reverse a file that's too long to store in RAM"
}
longfile_r_enomem_body()
{
# When we reverse a file that's too long for RAM, tail should drop the
# first part and just print what it can. We'll check that the last
# part is ok
{
ulimit -v 32768 || atf_skip "Can't adjust ulimit"
jot -w "%01023d" 32768 0 | tail -r > outfile ;
}
if [ "$?" -ne 1 ]; then
atf_skip "Didn't get ENOMEM. Adjust test parameters"
fi
# We don't know how much of the input we dropped. So just check that
# the first ten lines of tail's output are the same as the last ten of
# the input
jot -w "%01023d" 10 32767 0 -1 > expectfile
head -n 10 outfile > outtrunc
diff expectfile outtrunc
atf_check cmp expectfile outtrunc
}
atf_test_case longfile_r_longlines
longfile_r_longlines_head()
{
atf_set "descr" "Reverse a long file with extremely long lines"
}
longfile_r_longlines_body()
{
jot -s " " -w "%07d" 18000 0 > infile
jot -s " " -w "%07d" 18000 18000 >> infile
jot -s " " -w "%07d" 18000 36000 >> infile
jot -s " " -w "%07d" 18000 36000 > expectfile
jot -s " " -w "%07d" 18000 18000 >> expectfile
jot -s " " -w "%07d" 18000 0 >> expectfile
tail -r infile > outfile
tail -r < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case longfile_rc135782
longfile_rc135782_head()
{
atf_set "descr" "Reverse a long file and print the last 135,782 bytes"
}
longfile_rc135782_body()
{
jot -w "%063d" 9000 0 > infile
jot -w "%063d" 2121 8999 0 -1 > expectfile
echo "0000000000000000000000000000000006878" >> expectfile
tail -rc135782 infile > outfile
tail -rc135782 < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case longfile_rc145782_longlines
longfile_rc145782_longlines_head()
{
atf_set "descr" "Reverse a long file with extremely long lines and print the last 145,782 bytes"
}
longfile_rc145782_longlines_body()
{
jot -s " " -w "%07d" 18000 0 > infile
jot -s " " -w "%07d" 18000 18000 >> infile
jot -s " " -w "%07d" 18000 36000 >> infile
jot -s " " -w "%07d" 18000 36000 > expectfile
echo -n "35777 " >> expectfile
jot -s " " -w "%07d" 222 35778 >> expectfile
tail -rc145782 infile > outfile
tail -rc145782 < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case longfile_rn2500
longfile_rn2500_head()
{
atf_set "descr" "Reverse a long file and print the last 2,500 lines"
}
longfile_rn2500_body()
{
jot -w "%063d" 9000 0 > infile
jot -w "%063d" 2500 8999 0 -1 > expectfile
tail -rn2500 infile > outfile
tail -rn2500 < infile > outpipe
atf_check cmp expectfile outfile
atf_check cmp expectfile outpipe
}
atf_test_case broken_pipe
broken_pipe_head()
{
atf_set "descr" "Do not print bogus errno based output on short writes"
}
broken_pipe_body()
{
atf_check -o save:ints seq -f '%128g' 1 1000
atf_check -s ignore \
-e "inline:tail: stdout\nexit code: 1\n" \
-x '(tail -n 856 ints; echo exit code: $? >&2) | sleep 2'
}
atf_test_case stdin
stdin_head()
{
atf_set "descr" "Check basic operations on standard input"
}
stdin_body()
{
seq 1 5 > infile
seq 1 5 > expectfile
seq 5 1 > expectfile_r
tail < infile > outfile
tail -r < infile > outfile_r
atf_check cmp expectfile outfile
atf_check cmp expectfile_r outfile_r
}
atf_test_case follow
follow_head()
{
atf_set "descr" "Basic regression test for -f"
}
follow_body()
{
local pid
seq 1 5 > expectfile
seq 1 3 > infile
tail -f infile > outfile &
pid=$!
sleep 0.1
seq 4 5 >> infile
sleep 0.1
atf_check cmp expectfile outfile
atf_check kill $pid
}
atf_test_case follow_stdin
follow_stdin_head()
{
atf_set "descr" "Verify that -f works with files piped to standard input"
}
follow_stdin_body()
{
local pid
seq 1 5 > expectfile
seq 1 3 > infile
tail -f < infile > outfile &
pid=$!
sleep 0.1
seq 4 5 >> infile
sleep 0.1
atf_check cmp expectfile outfile
atf_check kill $pid
}
atf_test_case follow_create
follow_create_head()
{
atf_set "descr" "Verify that -F works when a file is created"
}
follow_create_body()
{
local pid
rm -f infile
tail -F infile > outfile &
pid=$!
seq 1 5 >infile
sleep 2
atf_check cmp infile outfile
atf_check kill $pid
}
atf_test_case follow_rename
follow_rename_head()
{
atf_set "descr" "Verify that -F works when a file is replaced"
}
follow_rename_body()
{
local pid
seq 1 5 > expectfile
seq 1 3 > infile
tail -F infile > outfile &
pid=$!
seq 4 5 > infile_new
atf_check mv infile infile_old
atf_check mv infile_new infile
# tail -F polls for a new file every 1s.
sleep 2
atf_check cmp expectfile outfile
atf_check kill $pid
}
atf_test_case silent_header
silent_header_head() {
atf_set "descr" "Test tail(1)'s silent header feature"
}
silent_header_body() {
jot 11 1 11 > file1
jot 11 2 12 > file2
jot 10 2 11 > expectfile
jot 10 3 12 >> expectfile
tail -q file1 file2 > outfile
atf_check cmp outfile expectfile
}
atf_test_case verbose_header
verbose_header_head() {
atf_set "descr" "Test tail(1)'s verbose header feature"
}
verbose_header_body() {
jot 11 1 11 > file1
echo '==> file1 <==' > expectfile
jot 10 2 11 >> expectfile
tail -v file1 > outfile
atf_check cmp outfile expectfile
}
atf_test_case si_number
si_number_head() {
atf_set "descr" "Test tail(1)'s SI number feature"
}
si_number_body() {
jot -b aaaaaaa 129 > file1
jot -b aaaaaaa 128 > expectfile
tail -c 1k file1 > outfile
atf_check cmp outfile expectfile
jot 1025 1 1025 > file1
jot 1024 2 1025 > expectfile
tail -n 1k file1 > outfile
atf_check cmp outfile expectfile
}
atf_test_case no_lf_at_eof
no_lf_at_eof_head()
{
atf_set "descr" "File does not end in newline"
}
no_lf_at_eof_body()
{
printf "a\nb\nc" >infile
atf_check -o inline:"c" tail -1 infile
atf_check -o inline:"b\nc" tail -2 infile
atf_check -o inline:"a\nb\nc" tail -3 infile
atf_check -o inline:"a\nb\nc" tail -4 infile
}
atf_init_test_cases()
{
atf_add_test_case empty_r
atf_add_test_case file_r
atf_add_test_case file_rc28
atf_add_test_case file_rn2
atf_add_test_case pipe_leading_newline_r
# The longfile tests are designed to exercise behavior in r_buf(),
# which operates on 128KB blocks
atf_add_test_case longfile_r
atf_add_test_case longfile_r_enomem
atf_add_test_case longfile_r_longlines
atf_add_test_case longfile_rc135782
atf_add_test_case longfile_rc145782_longlines
atf_add_test_case longfile_rn2500
atf_add_test_case broken_pipe
atf_add_test_case stdin
atf_add_test_case follow
atf_add_test_case follow_stdin
atf_add_test_case follow_create
atf_add_test_case follow_rename
atf_add_test_case silent_header
atf_add_test_case verbose_header
atf_add_test_case si_number
atf_add_test_case no_lf_at_eof
}