diff --git a/pkgs/ruby/spec/lib/kernaux/cmdline_spec.rb b/pkgs/ruby/spec/lib/kernaux/cmdline_spec.rb index 44eddb77..f95b2660 100644 --- a/pkgs/ruby/spec/lib/kernaux/cmdline_spec.rb +++ b/pkgs/ruby/spec/lib/kernaux/cmdline_spec.rb @@ -61,7 +61,7 @@ RSpec.describe KernAux, '.cmdline' do end end - xcontext 'when there are too many args' do + context 'when there are too many args' do let(:str) { 'a ' * 257 } specify do @@ -70,7 +70,7 @@ RSpec.describe KernAux, '.cmdline' do end end - xcontext 'when args cause buffer overflow' do + context 'when args cause buffer overflow' do let(:str) { 'a' * 4096 } specify do diff --git a/src/cmdline.c b/src/cmdline.c index 8cb0f7e4..b2c714c6 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -17,10 +17,10 @@ enum State { bool kernaux_cmdline( const char *const cmdline, - char *error_msg, + char *const error_msg, size_t *const argc, - char **argv, - char *buffer, + char **const argv, + char *const buffer, const size_t argv_count_max, const size_t buffer_size ) { @@ -43,7 +43,6 @@ bool kernaux_cmdline( if (cmdline[0] == '\0') return true; enum State state = INITIAL; - size_t buffer_pos = 0; for (size_t index = 0; ; ++index) { @@ -65,7 +64,7 @@ bool kernaux_cmdline( } state = BACKSLASH; - argv[(*argc)++] = buffer; + argv[(*argc)++] = &buffer[buffer_pos]; } else if (cur == '"') { if (*argc >= argv_count_max) { strcpy(error_msg, "too many args"); @@ -73,7 +72,7 @@ bool kernaux_cmdline( } state = QUOTE; - argv[(*argc)++] = buffer; + argv[(*argc)++] = &buffer[buffer_pos]; } else { if (*argc >= argv_count_max) { strcpy(error_msg, "too many args"); @@ -86,9 +85,8 @@ bool kernaux_cmdline( } state = TOKEN; - argv[(*argc)++] = buffer; - *(buffer++) = cur; - ++buffer_pos; + argv[(*argc)++] = &buffer[buffer_pos]; + buffer[buffer_pos++] = cur; } break; @@ -104,7 +102,7 @@ bool kernaux_cmdline( } state = BACKSLASH; - argv[(*argc)++] = buffer; + argv[(*argc)++] = &buffer[buffer_pos]; } else if (cur == '"') { if (*argc >= argv_count_max) { strcpy(error_msg, "too many args"); @@ -112,7 +110,7 @@ bool kernaux_cmdline( } state = QUOTE; - argv[(*argc)++] = buffer; + argv[(*argc)++] = &buffer[buffer_pos]; } else { if (*argc >= argv_count_max) { strcpy(error_msg, "too many args"); @@ -125,9 +123,8 @@ bool kernaux_cmdline( } state = TOKEN; - argv[(*argc)++] = buffer; - *(buffer++) = cur; - ++buffer_pos; + argv[(*argc)++] = &buffer[buffer_pos]; + buffer[buffer_pos++] = cur; } break; @@ -139,8 +136,7 @@ bool kernaux_cmdline( } state = FINAL; - *(buffer++) = '\0'; - ++buffer_pos; + buffer[buffer_pos++] = '\0'; } else if (cur == ' ') { if (buffer_pos >= buffer_size) { strcpy(error_msg, "buffer overflow"); @@ -148,8 +144,7 @@ bool kernaux_cmdline( } state = WHITESPACE; - *(buffer++) = '\0'; - ++buffer_pos; + buffer[buffer_pos++] = '\0'; } else if (cur == '\\') { state = BACKSLASH; } else if (cur == '"') { @@ -161,8 +156,7 @@ bool kernaux_cmdline( goto fail; } - *(buffer++) = cur; - ++buffer_pos; + buffer[buffer_pos++] = cur; } break; @@ -177,8 +171,7 @@ bool kernaux_cmdline( } state = TOKEN; - *(buffer++) = cur; - ++buffer_pos; + buffer[buffer_pos++] = cur; } break; @@ -195,16 +188,14 @@ bool kernaux_cmdline( } state = WHITESPACE; - *(buffer++) = '\0'; - ++buffer_pos; + buffer[buffer_pos++] = '\0'; } else { if (buffer_pos >= buffer_size) { strcpy(error_msg, "buffer overflow"); goto fail; } - *(buffer++) = cur; - ++buffer_pos; + buffer[buffer_pos++] = cur; } break; @@ -219,8 +210,7 @@ bool kernaux_cmdline( } state = QUOTE; - *(buffer++) = cur; - ++buffer_pos; + buffer[buffer_pos++] = cur; } break; } diff --git a/tests/test_cmdline.c b/tests/test_cmdline.c index 886b87fd..de16d97d 100644 --- a/tests/test_cmdline.c +++ b/tests/test_cmdline.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #define ARGV_COUNT_MAX 100 @@ -231,6 +232,56 @@ int main() test("\"", 0, 0, false, "EOL inside quote", 0, NULL); test("\"foo", 0, 0, false, "EOL inside quote", 0, NULL); + test( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0, + 0, + true, + "", + 1, + NULL + ); + + test("a", 1, 0, true, "", 1, NULL); + test("a a", 1, 0, false, "too many args", 0, NULL); + test("a a ", 1, 0, false, "too many args", 0, NULL); + test("a a", 2, 0, true, "", 2, NULL); + test("a a ", 2, 0, true, "", 2, NULL); + test("a a a", 2, 0, false, "too many args", 0, NULL); + test("a a a ", 2, 0, false, "too many args", 0, NULL); + test("a a a", 3, 0, true, "", 3, NULL); + test("a a a ", 3, 0, true, "", 3, NULL); + test("a a a a", 3, 0, false, "too many args", 0, NULL); + test("a a a a ", 3, 0, false, "too many args", 0, NULL); + test("a a a a", 4, 0, true, "", 4, NULL); + test("a a a a ", 4, 0, true, "", 4, NULL); + test("a a a a a", 4, 0, false, "too many args", 0, NULL); + test("a a a a a ", 4, 0, false, "too many args", 0, NULL); + test("a a a a a", 5, 0, true, "", 5, NULL); + test("a a a a a ", 5, 0, true, "", 5, NULL); + test("a a a a a a", 5, 0, false, "too many args", 0, NULL); + test("a a a a a a ", 5, 0, false, "too many args", 0, NULL); + test("a a a a a a", 6, 0, true, "", 6, NULL); + test("a a a a a a ", 6, 0, true, "", 6, NULL); + + { + char *const buffer = malloc(4096); + memset(buffer, 'a', 4096 - 1); + buffer[4096 - 1] = '\0'; + // 4095 of "a" + test(buffer, 256, 4096, true, "", 1, NULL); + free(buffer); + } + + { + char *const buffer = malloc(4096 + 1); + memset(buffer, 'a', 4096); + buffer[4096] = '\0'; + // 4096 of "a" + test(buffer, 256, 4096, false, "buffer overflow", 0, NULL); + free(buffer); + } + return 0; } @@ -247,10 +298,18 @@ void test( if (argv_count_max == 0) argv_count_max = ARGV_COUNT_MAX; if (buffer_size == 0) buffer_size = BUFFER_SIZE; - char error_msg[KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX]; + char *error_msg = malloc(KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX); size_t argc = 1234; - char *argv[argv_count_max]; - char buffer[buffer_size]; + char **const argv = malloc(sizeof(char*) * argv_count_max); + char *const buffer = malloc(buffer_size); + + assert(error_msg); + assert(argv); + assert(buffer); + + memset(error_msg, 'a', KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX); + memset(argv, 'a', sizeof(char*) * argv_count_max); + memset(buffer, 'a', buffer_size); assert( kernaux_cmdline( @@ -267,11 +326,17 @@ void test( assert(strcmp(error_msg, expected_error_msg) == 0); assert(argc == expected_argc); - for (size_t index = 0; index < argc; ++index) { - assert(strcmp(argv[index], expected_argv[index]) == 0); + if (expected_argv) { + for (size_t index = 0; index < argc; ++index) { + assert(strcmp(argv[index], expected_argv[index]) == 0); + } } for (size_t index = argc; index < argv_count_max; ++index) { assert(argv[index] == NULL); } + + free(error_msg); + free(argv); + free(buffer); }