minishell/test.sh
Khaïs COLIN 0842fdfe1d
fix: double free when getting command path for empty command
```
minishell$ ""
```
leads to the following error:
```
=================================================================
==1158770==ERROR: AddressSanitizer: attempting double-free on 0x5060000020c0 in thread T0:
    #0 0x5649fcb5b8a8 in free.part.0 asan_malloc_linux.cpp.o
    #1 0x5649fcbac549 in deal_with_filled_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:119:3
    #2 0x5649fcbac38b in filepath_from_env /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:165:10
    #3 0x5649fcbac220 in get_cmdpath /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:186:10
    #4 0x5649fcba8664 in simple_cmd_execute /home/khais/src/42/common_env/minishell/src/executing/simple_cmd/simple_cmd_execute.c:71:8
    #5 0x5649fcba6e4c in execute_command /home/khais/src/42/common_env/minishell/src/minishell.c:49:2
    #6 0x5649fcba6ce5 in main /home/khais/src/42/common_env/minishell/src/minishell.c:92:3
    #7 0x7fb4f2dcd27d in __libc_start_call_main (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a27d) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)
    #8 0x7fb4f2dcd338 in __libc_start_main@GLIBC_2.2.5 (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a338) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)
    #9 0x5649fca6f394 in _start (/home/khais/src/42/common_env/minishell/minishell+0x2b394)

0x5060000020c0 is located 0 bytes inside of 58-byte region [0x5060000020c0,0x5060000020fa)
freed by thread T0 here:
    #0 0x5649fcb5b8a8 in free.part.0 asan_malloc_linux.cpp.o
    #1 0x5649fcbac669 in select_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:81:3
    #2 0x5649fcbac524 in deal_with_filled_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:116:9
    #3 0x5649fcbac38b in filepath_from_env /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:165:10
    #4 0x5649fcbac220 in get_cmdpath /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:186:10
    #5 0x5649fcba8664 in simple_cmd_execute /home/khais/src/42/common_env/minishell/src/executing/simple_cmd/simple_cmd_execute.c:71:8
    #6 0x5649fcba6e4c in execute_command /home/khais/src/42/common_env/minishell/src/minishell.c:49:2
    #7 0x5649fcba6ce5 in main /home/khais/src/42/common_env/minishell/src/minishell.c:92:3
    #8 0x7fb4f2dcd27d in __libc_start_call_main (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a27d) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)

previously allocated by thread T0 here:
    #0 0x5649fcb5c897 in malloc (/home/khais/src/42/common_env/minishell/minishell+0x118897)
    #1 0x5649fcbac6be in alloc_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp_utils.c:34:9
    #2 0x5649fcbac4e9 in deal_with_filled_path /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:113:15
    #3 0x5649fcbac38b in filepath_from_env /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:165:10
    #4 0x5649fcbac220 in get_cmdpath /home/khais/src/42/common_env/minishell/src/subst/simple_filename_exp.c:186:10
    #5 0x5649fcba8664 in simple_cmd_execute /home/khais/src/42/common_env/minishell/src/executing/simple_cmd/simple_cmd_execute.c:71:8
    #6 0x5649fcba6e4c in execute_command /home/khais/src/42/common_env/minishell/src/minishell.c:49:2
    #7 0x5649fcba6ce5 in main /home/khais/src/42/common_env/minishell/src/minishell.c:92:3
    #8 0x7fb4f2dcd27d in __libc_start_call_main (/nix/store/nqb2ns2d1lahnd5ncwmn6k84qfd7vx2k-glibc-2.40-36/lib/libc.so.6+0x2a27d) (BuildId: 704cab5816f130c494208e8cc24d87a386ef085b)

SUMMARY: AddressSanitizer: double-free asan_malloc_linux.cpp.o in free.part.0
==1158770==ABORTING
```

Looks like select_path frees filepath sometimes, no idea why. Removing these
lines didn't break any tests.

I'm pushing this to get a second opinion
2025-04-15 14:41:44 +02:00

350 lines
5.8 KiB
Bash
Executable file

#!/usr/bin/env bash
set -uo pipefail
declare -i FAILED=0
declare -i SUCCEDED=0
declare -i TODO=0
declare -i RAN=0
NAME=""
EXTRAENV=""
MINISHELL=$PWD/minishell
report_header()
{
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo $NAME
echo "++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo "++++ test input:"
}
failed_report()
{
NAME="failed: $NAME"
report_header
cat /tmp/input.minishell
echo "++++ Got output:"
cat /tmp/got.minishell
echo "++++ But expected:"
cat /tmp/expected.minishell
echo "++++ Diff:"
echo "(red is got, green is expected)"
diff --color=always /tmp/got.minishell /tmp/expected.minishell
echo "++++ Run this to debug:"
cat < /tmp/input.minishell > /tmp/input.failed.$FAILED.minishell
echo "cat /tmp/input.failed.$FAILED.minishell | ./minishell"
}
todo_report()
{
NAME="todo: $NAME"
report_header
cat /tmp/input.minishell
echo "++++ Got output:"
cat /tmp/got.minishell
echo "++++ But test was marked as todo."
echo "++++ Run this to debug:"
cat < /tmp/input.minishell > /tmp/input.todo.$TODO.minishell
echo "cat /tmp/input.todo.$TODO.minishell | ./minishell"
}
failed()
{
echo -n "F"
FAILED+=1
failed_report >> report.txt
}
succeded()
{
echo -n "."
SUCCEDED+=1
}
assert()
{
diff -q /tmp/got.minishell /tmp/expected.minishell > /dev/null && succeded || failed
}
when_run()
{
cat > /tmp/input.minishell
rm -rf /tmp/dir.minishell
mkdir -p /tmp/dir.minishell
pushd /tmp/dir.minishell > /dev/null
env $EXTRAENV $MINISHELL &> /tmp/got.minishell < /tmp/input.minishell
popd > /dev/null
EXTRAENV=""
RAN+=1
NAME=$1
}
expecting()
{
cat > /tmp/expected.minishell
assert
}
todo()
{
echo -n "t"
TODO+=1
todo_report >> report.txt
}
setup()
{
rm -f report.txt
}
finalize()
{
echo
echo "$RAN tests ran. $SUCCEDED succeded, $FAILED failed, $TODO todo."
if [ $SUCCEDED -eq $RAN ];
then
echo "All integration tests passed!"
else
echo "cat report.txt"
echo "to see details of failed/todo tests"
fi
exit $FAILED
}
setup
when_run <<EOF "empty inputs returns nothing"
EOF
expecting <<EOF
EOF
when_run <<EOF "simple commands are run"
echo no files:
ls -a
touch t
echo single file:
ls -a
EOF
expecting <<EOF
no files:
.
..
single file:
.
..
t
EOF
when_run <<EOF "unknown commands"
qwertyuiop
poiuytrewq
EOF
expecting <<EOF
minishell: qwertyuiop: command not found
minishell: poiuytrewq: command not found
EOF
when_run <<EOF "pwd works"
pwd
pwd hello
pwd hello there
pwd -O
pwd -O hello
pwd there -O hello
EOF
expecting <<EOF
/tmp/dir.minishell
/tmp/dir.minishell
/tmp/dir.minishell
/tmp/dir.minishell
/tmp/dir.minishell
/tmp/dir.minishell
EOF
when_run <<EOF "pwd works in any directory, can cd to absolute paths"
mkdir test
cd /tmp/dir.minishell/test
pwd
mkdir hi
cd /tmp/dir.minishell/test/hi
pwd
cd /tmp/dir.minishell/test
pwd
EOF
expecting <<EOF
/tmp/dir.minishell/test
/tmp/dir.minishell/test/hi
/tmp/dir.minishell/test
EOF
when_run <<EOF "cd will print error when going into a directory with no access"
mkdir test
chmod -x test
cd test
pwd
chmod +x test
EOF
expecting <<EOF
minishell: cd: Permission denied
/tmp/dir.minishell
EOF
EXTRAENV="HOME=/tmp/dir.minishell/fakehome"
when_run <<EOF "cd with no arguments goes to \$HOME"
mkdir fakehome
cd
pwd
EOF
expecting <<EOF
/tmp/dir.minishell/fakehome
EOF
EXTRAENV="-u HOME"
when_run <<EOF "cd with no arguments, with HOME unset, prints error message and does nothing"
cd
pwd
EOF
expecting <<EOF
minishell: cd: HOME not set
/tmp/dir.minishell
EOF
when_run <<EOF "cd with empty argument"
cd ""
pwd
EOF
expecting <<EOF
/tmp/dir.minishell
EOF
EXTRAENV="HOME="
when_run <<EOF "cd with no arguments and HOME equal to empty string"
cd
pwd
EOF
expecting <<EOF
/tmp/dir.minishell
EOF
when_run <<EOF "cd with simple relative paths"
mkdir test
cd test
pwd
cd ..
pwd
EOF
expecting <<EOF
/tmp/dir.minishell/test
/tmp/dir.minishell
EOF
when_run <<EOF "cd with complicated relative paths"
mkdir -p test/hello/there
mkdir -p test/hi/there
mkdir -p test/hello/world
cd test/hello/there
pwd
cd ../../hi
pwd
cd ../hi/../hi/../hi/../hi
pwd
cd ../hi/../hi/../hi/../hi/../../test/hello/world
pwd
EOF
expecting <<EOF
/tmp/dir.minishell/test/hello/there
/tmp/dir.minishell/test/hi
/tmp/dir.minishell/test/hi
/tmp/dir.minishell/test/hello/world
EOF
when_run <<EOF "cd print error when given too many arguments"
cd hello there
pwd
cd . ..
pwd
EOF
expecting <<EOF
minishell: cd: too many arguments
/tmp/dir.minishell
minishell: cd: too many arguments
/tmp/dir.minishell
EOF
EXTRAENV="VAR=value hello=bye"
when_run <<"EOF" "variable substitution works"
echo $VAR $hello
EOF
expecting <<EOF
value bye
EOF
when_run <<"EOF" "export works with single variable"
export var=testvalue
echo var=$var
export var="Another Test Value"
echo var=$var
EOF
expecting <<EOF
var=testvalue
var=Another Test Value
EOF
when_run <<"EOF" "export works with multiple variables"
export name="Sir Reginald Pikedevant" greeting="Well met," counter=27 "question=How many fingers am I holding up?"
echo $greeting $name
echo $question
echo Umm, $counter?
EOF
expecting <<EOF
Well met, Sir Reginald Pikedevant
How many fingers am I holding up?
Umm, 27?
EOF
EXTRAENV=-i
when_run <<EOF "export with strange inputs"
export PATH=$PATH
export var
echo status=\$?
echo var=[\$var]
export blue=
echo status=\$?
echo blue=[\$blue]
env -u PATH env
EOF
expecting <<EOF
status=0
var=[]
status=0
blue=[]
blue=
EOF
when_run <<"EOF" "export with invalid identifiers"
export = 123=456 hello=hi 456=789
echo status=$?
echo hello=$hello
EOF
expecting <<"EOF"
minishell: export: `=': not a valid identifier
minishell: export: `123=456': not a valid identifier
minishell: export: `456=789': not a valid identifier
status=1
hello=hi
EOF
when_run <<EOF "empty command is not found"
""
EOF
expecting <<EOF
minishell: : command not found
EOF
when_run <<EOF "quoted parentheses are not operators"
echo unclosed '('
EOF
todo
finalize