summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Phillips <david@sighup.nz>2019-08-11 17:22:20 +1200
committerDavid Phillips <david@sighup.nz>2019-08-11 17:22:20 +1200
commit2d2628df0001d2b643811dc69717c4afe70c4e31 (patch)
treef5d0da23c47c3e677ca423a91838c5ad5467a5d9
parent0e6f47211a0516cf2c96d2b9e89c0d841978c143 (diff)
downloadtoy-cpu-assembler-2d2628df0001d2b643811dc69717c4afe70c4e31.tar.xz
Fix and add tests for NULL deref cases found by scan-build
-rw-r--r--assembler.c3
-rw-r--r--lex.c18
-rw-r--r--lex.h3
-rw-r--r--parse.c7
-rw-r--r--test/Makefile1
-rwxr-xr-xtest/asm/run-asm.sh59
-rw-r--r--test/asm/should-fail/001-segfault-li-comma-end.asm2
-rw-r--r--test/asm/should-fail/002-segfault-b-short.asm2
-rw-r--r--test/asm/should-fail/003-segfault-j-short.asm2
-rw-r--r--test/asm/should-fail/004-segfault-imm-short.asm2
-rwxr-xr-xtest/full-pipeline/run-full-pipeline.sh21
-rw-r--r--test/full-pipeline/should-pass/001-nop.asm (renamed from test/full-pipeline/001-nop.asm)0
-rw-r--r--test/full-pipeline/should-pass/002-nops.asm (renamed from test/full-pipeline/002-nops.asm)0
-rw-r--r--test/full-pipeline/should-pass/003-rtype.asm (renamed from test/full-pipeline/003-rtype.asm)0
-rw-r--r--test/full-pipeline/should-pass/004-itype.asm (renamed from test/full-pipeline/004-itype.asm)0
-rw-r--r--test/full-pipeline/should-pass/005-small-loop.asm (renamed from test/full-pipeline/005-small-loop.asm)0
-rw-r--r--test/full-pipeline/should-pass/006-2-inst-2-words.asm (renamed from test/full-pipeline/006-2-inst-2-words.asm)0
-rw-r--r--test/full-pipeline/should-pass/007-3-inst-3-words.asm (renamed from test/full-pipeline/007-3-inst-3-words.asm)0
-rw-r--r--test/full-pipeline/should-pass/008-3-inst-5-words.asm (renamed from test/full-pipeline/008-3-inst-5-words.asm)0
-rw-r--r--test/full-pipeline/should-pass/009-3-inst-5-words-rev.asm (renamed from test/full-pipeline/009-3-inst-5-words-rev.asm)0
-rw-r--r--test/full-pipeline/should-pass/010-empty.asm (renamed from test/full-pipeline/010-empty.asm)0
-rw-r--r--util.c5
22 files changed, 101 insertions, 24 deletions
diff --git a/assembler.c b/assembler.c
index bd94c33..bc6d7dd 100644
--- a/assembler.c
+++ b/assembler.c
@@ -63,8 +63,6 @@ int main(int argc, char **argv)
if ((tokens = lex(path_in, fin, &tok_count)) == NULL)
return error_ret;
-
- fclose(fin);
debug("Lexed.\n");
/* FIXME package these things into `tok_result`, `parse_result` etc */
@@ -86,6 +84,7 @@ int main(int argc, char **argv)
parse_free(insts, insts_count, labels, labels_count);
lex_free(tokens, tok_count);
+ fclose(fin);
fclose(fout);
debug("Output.\n");
diff --git a/lex.c b/lex.c
index 6ed8007..29cd8c2 100644
--- a/lex.c
+++ b/lex.c
@@ -296,6 +296,13 @@ static int lex_eol(struct token *t) {
return 0;
}
+static int lex_eof(struct token *t) {
+ t->type = TOKEN_EOF;
+ t->span = 1;
+ store_location(t);
+ return 0;
+}
+
int lex_line(void) {
int ret = 0;
size_t len = strlen(buffer);
@@ -424,12 +431,11 @@ struct token* lex(const char *filename_local, FILE *fin, size_t *len)
return NULL;
}
- /* no tokens? just an EOL then */
- if (tokens_count == 0) {
- struct token eol = {0};
- lex_eol(&eol);
- add_token(eol);
- }
+ /* tack on EOF */
+ line--; column--;
+ struct token eof = {0};
+ lex_eof(&eof);
+ add_token(eof);
*len = tokens_count;
return tokens;
diff --git a/lex.h b/lex.h
index 908d23e..389a5d7 100644
--- a/lex.h
+++ b/lex.h
@@ -12,7 +12,8 @@ enum TOKEN_TYPE {
TOKEN_STRING, /* string literal */
TOKEN_NUMERIC, /* numeric literal, incl literal chars */
TOKEN_REGISTER, /* $0, $H, $1 */
- TOKEN_EOL /* end of line */
+ TOKEN_EOL, /* end of line */
+ TOKEN_EOF, /* end of file */
};
struct token {
diff --git a/parse.c b/parse.c
index 2bfc7da..a091939 100644
--- a/parse.c
+++ b/parse.c
@@ -22,6 +22,7 @@ static size_t labels_count;
static struct instruction *insts;
static size_t insts_count;
static size_t byte_offset;
+static struct token token_eof = { .type = TOKEN_EOF };
static void emit(const char *fmt, ...)
{
@@ -59,7 +60,7 @@ static int expect(enum TOKEN_TYPE e)
if (cursor) {
observed_desc = get_token_description(cursor->type);
} else {
- observed_desc = "end of file";
+ observed_desc = "(error: unknown)";
}
emit("Error: Expected %s, got %s\n", expected_desc, observed_desc);
return 1;
@@ -73,7 +74,7 @@ void kerchunk()
if (tokens_pos < tokens_count - 1) {
cursor = &tokens[++tokens_pos];
} else {
- cursor = NULL;
+ cursor = &token_eof;
}
}
@@ -500,7 +501,7 @@ int parse(const char *filename_local, FILE* fd_local, struct label **labels_loca
byte_offset = 0;
cursor = tokens;
- while (cursor) {
+ while (cursor && cursor->type != TOKEN_EOF) {
switch(cursor->type) {
case TOKEN_EOL:
kerchunk();
diff --git a/test/Makefile b/test/Makefile
index cb0f3ee..083802c 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,3 +1,4 @@
test:
+ ./asm/run-asm.sh
./full-pipeline/run-full-pipeline.sh
./emul/run-emul.sh
diff --git a/test/asm/run-asm.sh b/test/asm/run-asm.sh
new file mode 100755
index 0000000..7574abb
--- /dev/null
+++ b/test/asm/run-asm.sh
@@ -0,0 +1,59 @@
+#!/bin/bash -e
+
+#
+# Script for running all of the automated which will go from source to binary.
+#
+
+fail() {
+ echo -e '[\e[1;31mFAIL\e[0m] '"$1:" "$2"
+ has_failure=1
+}
+
+pass() {
+ echo -e '[\e[1;32mPASS\e[0m] '"$1"
+}
+
+clean() {
+ echo "Removing work dir $WORK"
+ rm -r "$WORK"
+}
+
+if [ "$1" == "noclean" ]; then
+ NO_CLEAN=1
+else
+ NO_CLEAN=0
+fi
+WORK=$(mktemp -d)
+pushd $(dirname "$0") >/dev/null
+source ../valgrind.sh
+export ASM="$PWD/../../assembler"
+export DISASM="$PWD/../../disassembler"
+has_failure=0
+
+for first_stage_asm in should-fail/*.asm ; do
+ t=$(basename "$first_stage_asm")
+ first_stage_bin="$WORK/${t}-first_stage.bin"
+ log="$WORK/${t}.log"
+
+ # Assemble test code
+ set +e
+ $VALGRIND $VALGRIND_OPTS "$ASM" "$first_stage_asm" "$first_stage_bin" 2>"$log"
+ xc="$?"
+ set -e
+ if (( xc > 0 && xc < 128 )); then
+ pass "$t" "assembly xfailed"
+ elif (( xc == 0 )); then
+ fail "$t" "assembly didn't fail as expected"
+ else
+ fail "$t" "assembler was sent signal $(( xc - 128 ))"
+ fi
+done
+popd >/dev/null
+
+if [[ "$failure" != "0" && "$NO_CLEAN" == "1" ]] ; then
+ echo "Warning: Leaving work dir $WORK in place. Please remove this yourself"
+else
+ clean
+fi
+
+exit "$has_failure"
diff --git a/test/asm/should-fail/001-segfault-li-comma-end.asm b/test/asm/should-fail/001-segfault-li-comma-end.asm
new file mode 100644
index 0000000..bde586c
--- /dev/null
+++ b/test/asm/should-fail/001-segfault-li-comma-end.asm
@@ -0,0 +1,2 @@
+; Test for bug previously found with scan-build. Important: no CR/LF at EOF
+ldi $1, \ No newline at end of file
diff --git a/test/asm/should-fail/002-segfault-b-short.asm b/test/asm/should-fail/002-segfault-b-short.asm
new file mode 100644
index 0000000..898a3b0
--- /dev/null
+++ b/test/asm/should-fail/002-segfault-b-short.asm
@@ -0,0 +1,2 @@
+; Test for bug previously found with scan-build. Important: no CR/LF at EOF
+bnz \ No newline at end of file
diff --git a/test/asm/should-fail/003-segfault-j-short.asm b/test/asm/should-fail/003-segfault-j-short.asm
new file mode 100644
index 0000000..0b1486b
--- /dev/null
+++ b/test/asm/should-fail/003-segfault-j-short.asm
@@ -0,0 +1,2 @@
+; Test for bug previously found with scan-build. Important: no CR/LF at EOF
+jmp \ No newline at end of file
diff --git a/test/asm/should-fail/004-segfault-imm-short.asm b/test/asm/should-fail/004-segfault-imm-short.asm
new file mode 100644
index 0000000..42e8c4b
--- /dev/null
+++ b/test/asm/should-fail/004-segfault-imm-short.asm
@@ -0,0 +1,2 @@
+; Test for bug previously found with scan-build. Important: no CR/LF at EOF
+addi $0, $0, \ No newline at end of file
diff --git a/test/full-pipeline/run-full-pipeline.sh b/test/full-pipeline/run-full-pipeline.sh
index 25f7bf7..c5a1700 100755
--- a/test/full-pipeline/run-full-pipeline.sh
+++ b/test/full-pipeline/run-full-pipeline.sh
@@ -11,6 +11,7 @@
fail() {
echo -e '[\e[1;31mFAIL\e[0m] '"$1:" "$2"
+ has_failure=1
}
pass() {
@@ -34,33 +35,33 @@ export ASM="$PWD/../../assembler"
export DISASM="$PWD/../../disassembler"
has_failure=0
-for first_stage_asm in *.asm ; do
- first_stage_bin="$WORK/${first_stage_asm}-first_stage.bin"
- second_stage_asm="$WORK/${first_stage_asm}-second_stage.asm"
- second_stage_bin="$WORK/${first_stage_asm}-second_stage.bin"
+for first_stage_asm in should-pass/*.asm ; do
+ t=$(basename "$first_stage_asm")
+ first_stage_bin="$WORK/${t}-first_stage.bin"
+ second_stage_asm="$WORK/${t}-second_stage.asm"
+ second_stage_bin="$WORK/${t}-second_stage.bin"
# Assemble test code
if ! $VALGRIND $VALGRIND_OPTS "$ASM" "$first_stage_asm" "$first_stage_bin" ; then
- fail "$first_stage_asm" "first stage assembly failed"
+ fail "$t" "first stage assembly failed"
continue
fi
# Disassemble test code and re-assemble that disassembly
if ! $VALGRIND $VALGRIND_OPTS "$DISASM" "$first_stage_bin" "$second_stage_asm" ; then
- fail "$first_stage_asm" "first stage disassembly failed"
+ fail "$t" "first stage disassembly failed"
continue
fi
if ! $VALGRIND $VALGRIND_OPTS "$ASM" "$second_stage_asm" "$second_stage_bin" ; then
- fail "$first_stage_asm" "second stage assembly failed"
+ fail "$t" "second stage assembly failed"
continue
fi
# first stage bin and second stage identical for test pass
if diff "$first_stage_bin" "$second_stage_bin" >/dev/null; then
- pass "$first_stage_asm"
+ pass "$t"
else
- fail "$first_stage_asm" "binary mismatch"
- has_failure=1
+ fail "$t" "binary mismatch"
fi
done
diff --git a/test/full-pipeline/001-nop.asm b/test/full-pipeline/should-pass/001-nop.asm
index c27745a..c27745a 100644
--- a/test/full-pipeline/001-nop.asm
+++ b/test/full-pipeline/should-pass/001-nop.asm
diff --git a/test/full-pipeline/002-nops.asm b/test/full-pipeline/should-pass/002-nops.asm
index 45a3cbf..45a3cbf 100644
--- a/test/full-pipeline/002-nops.asm
+++ b/test/full-pipeline/should-pass/002-nops.asm
diff --git a/test/full-pipeline/003-rtype.asm b/test/full-pipeline/should-pass/003-rtype.asm
index 7ed5f78..7ed5f78 100644
--- a/test/full-pipeline/003-rtype.asm
+++ b/test/full-pipeline/should-pass/003-rtype.asm
diff --git a/test/full-pipeline/004-itype.asm b/test/full-pipeline/should-pass/004-itype.asm
index 4fbc032..4fbc032 100644
--- a/test/full-pipeline/004-itype.asm
+++ b/test/full-pipeline/should-pass/004-itype.asm
diff --git a/test/full-pipeline/005-small-loop.asm b/test/full-pipeline/should-pass/005-small-loop.asm
index 5c47e51..5c47e51 100644
--- a/test/full-pipeline/005-small-loop.asm
+++ b/test/full-pipeline/should-pass/005-small-loop.asm
diff --git a/test/full-pipeline/006-2-inst-2-words.asm b/test/full-pipeline/should-pass/006-2-inst-2-words.asm
index c4e2dbc..c4e2dbc 100644
--- a/test/full-pipeline/006-2-inst-2-words.asm
+++ b/test/full-pipeline/should-pass/006-2-inst-2-words.asm
diff --git a/test/full-pipeline/007-3-inst-3-words.asm b/test/full-pipeline/should-pass/007-3-inst-3-words.asm
index 717a122..717a122 100644
--- a/test/full-pipeline/007-3-inst-3-words.asm
+++ b/test/full-pipeline/should-pass/007-3-inst-3-words.asm
diff --git a/test/full-pipeline/008-3-inst-5-words.asm b/test/full-pipeline/should-pass/008-3-inst-5-words.asm
index c0a33b5..c0a33b5 100644
--- a/test/full-pipeline/008-3-inst-5-words.asm
+++ b/test/full-pipeline/should-pass/008-3-inst-5-words.asm
diff --git a/test/full-pipeline/009-3-inst-5-words-rev.asm b/test/full-pipeline/should-pass/009-3-inst-5-words-rev.asm
index 5592edd..5592edd 100644
--- a/test/full-pipeline/009-3-inst-5-words-rev.asm
+++ b/test/full-pipeline/should-pass/009-3-inst-5-words-rev.asm
diff --git a/test/full-pipeline/010-empty.asm b/test/full-pipeline/should-pass/010-empty.asm
index e69de29..e69de29 100644
--- a/test/full-pipeline/010-empty.asm
+++ b/test/full-pipeline/should-pass/010-empty.asm
diff --git a/util.c b/util.c
index a4b6949..e291f90 100644
--- a/util.c
+++ b/util.c
@@ -36,6 +36,7 @@ static struct {
{ .look = TOKEN_IDENT , .str = "identifier" },
{ .look = TOKEN_DOT , .str = "assembler directive" },
{ .look = TOKEN_EOL , .str = "end of line" },
+ { .look = TOKEN_EOF , .str = "end of file" },
{ .str = NULL },
};
@@ -191,8 +192,8 @@ void indicate_file_area(FILE* fd, size_t line, size_t column, size_t span)
fputs(margin, stderr);
fputs(s, stderr);
- /* corner case (still needed?) - buf was just return */
- if (strlen(buf) == 1 && buf[0] == '\n') {
+ /* corner case: buf had no return */
+ if (s[strlen(s) - 1] != '\n') {
fputc('\n', stderr);
}