Module Name: src Committed By: rillig Date: Sat Mar 2 11:56:37 UTC 2024
Modified Files: src/distrib/sets/lists/tests: mi src/tests/usr.bin/xlint/lint1: msg_369.c msg_370.c msg_372.c msg_373.c msg_375.c t_usage.sh src/usr.bin/xlint/lint1: cksnprintb.c err.c Added Files: src/tests/usr.bin/xlint/lint1: msg_376.c Log Message: lint: check snprintb formats for overlapping bits and fields To generate a diff of this commit: cvs rdiff -u -r1.1307 -r1.1308 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.1 -r1.2 src/tests/usr.bin/xlint/lint1/msg_369.c \ src/tests/usr.bin/xlint/lint1/msg_370.c \ src/tests/usr.bin/xlint/lint1/msg_372.c \ src/tests/usr.bin/xlint/lint1/msg_373.c \ src/tests/usr.bin/xlint/lint1/msg_375.c cvs rdiff -u -r0 -r1.1 src/tests/usr.bin/xlint/lint1/msg_376.c cvs rdiff -u -r1.16 -r1.17 src/tests/usr.bin/xlint/lint1/t_usage.sh cvs rdiff -u -r1.2 -r1.3 src/usr.bin/xlint/lint1/cksnprintb.c cvs rdiff -u -r1.227 -r1.228 src/usr.bin/xlint/lint1/err.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.1307 src/distrib/sets/lists/tests/mi:1.1308 --- src/distrib/sets/lists/tests/mi:1.1307 Fri Mar 1 19:39:28 2024 +++ src/distrib/sets/lists/tests/mi Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1307 2024/03/01 19:39:28 rillig Exp $ +# $NetBSD: mi,v 1.1308 2024/03/02 11:56:37 rillig Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -7490,6 +7490,7 @@ ./usr/tests/usr.bin/xlint/lint1/msg_373.c tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/msg_374.c tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/msg_375.c tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/xlint/lint1/msg_376.c tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/op_colon.c tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/xlint/lint1/op_colon.exp tests-obsolete obsolete,atf ./usr/tests/usr.bin/xlint/lint1/op_shl_lp64.c tests-usr.bin-tests compattestfile,atf Index: src/tests/usr.bin/xlint/lint1/msg_369.c diff -u src/tests/usr.bin/xlint/lint1/msg_369.c:1.1 src/tests/usr.bin/xlint/lint1/msg_369.c:1.2 --- src/tests/usr.bin/xlint/lint1/msg_369.c:1.1 Fri Mar 1 19:39:29 2024 +++ src/tests/usr.bin/xlint/lint1/msg_369.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: msg_369.c,v 1.1 2024/03/01 19:39:29 rillig Exp $ */ +/* $NetBSD: msg_369.c,v 1.2 2024/03/02 11:56:37 rillig Exp $ */ # 3 "msg_369.c" // Test for message: bit position '%.*s' in '%.*s' should be escaped as octal or hex [369] @@ -42,11 +42,11 @@ example(unsigned u32, uint64_t u64) "b\nnewline\0", u64); - /* expect+6: warning: bit position '\t' in 'f\t\010tab\0' should be escaped as octal or hex [369] */ - /* expect+5: warning: bit position '\n' in 'F\n\010newline\0' should be escaped as octal or hex [369] */ + /* expect+6: warning: bit position '\t' in 'f\t\001tab\0' should be escaped as octal or hex [369] */ + /* expect+5: warning: bit position '\n' in 'F\n\001newline\0' should be escaped as octal or hex [369] */ snprintb(buf, sizeof(buf), "\177\020" - "f\t\010tab\0" - "F\n\010newline\0", + "f\t\001tab\0" + "F\n\001newline\0", u64); } Index: src/tests/usr.bin/xlint/lint1/msg_370.c diff -u src/tests/usr.bin/xlint/lint1/msg_370.c:1.1 src/tests/usr.bin/xlint/lint1/msg_370.c:1.2 --- src/tests/usr.bin/xlint/lint1/msg_370.c:1.1 Fri Mar 1 19:39:29 2024 +++ src/tests/usr.bin/xlint/lint1/msg_370.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: msg_370.c,v 1.1 2024/03/01 19:39:29 rillig Exp $ */ +/* $NetBSD: msg_370.c,v 1.2 2024/03/02 11:56:37 rillig Exp $ */ # 3 "msg_370.c" // Test for message: field width '%.*s' in '%.*s' should be escaped as octal or hex [370] @@ -34,6 +34,7 @@ example(uint64_t u64) "f\t\ttab\0" "f\n\nnewline\0", u64); + /* expect-1: warning: 'f\n\nnewline\0' overlaps earlier 'f\t\ttab\0' on bit 10 [376] */ /* expect+11: warning: bit position ' ' in 'F space\0' should be escaped as octal or hex [369] */ /* expect+10: warning: field width ' ' in 'F space\0' should be escaped as octal or hex [370] */ @@ -47,4 +48,5 @@ example(uint64_t u64) "F\t\ttab\0" "F\n\nnewline\0", u64); + /* expect-1: warning: 'F\n\nnewline\0' overlaps earlier 'F\t\ttab\0' on bit 10 [376] */ } Index: src/tests/usr.bin/xlint/lint1/msg_372.c diff -u src/tests/usr.bin/xlint/lint1/msg_372.c:1.1 src/tests/usr.bin/xlint/lint1/msg_372.c:1.2 --- src/tests/usr.bin/xlint/lint1/msg_372.c:1.1 Fri Mar 1 19:39:29 2024 +++ src/tests/usr.bin/xlint/lint1/msg_372.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: msg_372.c,v 1.1 2024/03/01 19:39:29 rillig Exp $ */ +/* $NetBSD: msg_372.c,v 1.2 2024/03/02 11:56:37 rillig Exp $ */ # 3 "msg_372.c" // Test for message: field width '%.*s' (%ju) in '%.*s' out of range 0..%u [372] @@ -20,9 +20,10 @@ example(uint64_t u64) { char buf[64]; - /* expect+11: warning: field width '\101' (65) in 'f\000\101all+1\0' out of range 0..64 [372] */ - /* expect+10: warning: bit field end 65 in 'f\000\101all+1\0' out of range 0..64 [373] */ - /* expect+9: warning: bit field end 65 in 'f\001\100oob64\0' out of range 0..64 [373] */ + /* expect+12: warning: field width '\101' (65) in 'f\000\101all+1\0' out of range 0..64 [372] */ + /* expect+11: warning: bit field end 65 in 'f\000\101all+1\0' out of range 0..64 [373] */ + /* expect+10: warning: bit field end 65 in 'f\001\100oob64\0' out of range 0..64 [373] */ + /* expect+9: warning: 'f\001\100oob64\0' overlaps earlier 'f\000\100all\0' on bit 1 [376] */ /* expect+8: warning: field width '\377' (255) in 'f\010\377oob64\0' out of range 0..64 [372] */ /* expect+7: warning: bit field end 263 in 'f\010\377oob64\0' out of range 0..64 [373] */ snprintb(buf, sizeof(buf), Index: src/tests/usr.bin/xlint/lint1/msg_373.c diff -u src/tests/usr.bin/xlint/lint1/msg_373.c:1.1 src/tests/usr.bin/xlint/lint1/msg_373.c:1.2 --- src/tests/usr.bin/xlint/lint1/msg_373.c:1.1 Fri Mar 1 19:39:29 2024 +++ src/tests/usr.bin/xlint/lint1/msg_373.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: msg_373.c,v 1.1 2024/03/01 19:39:29 rillig Exp $ */ +/* $NetBSD: msg_373.c,v 1.2 2024/03/02 11:56:37 rillig Exp $ */ # 3 "msg_373.c" // Test for message: bit field end %ju in '%.*s' out of range 0..64 [373] @@ -21,9 +21,10 @@ example(uint64_t u64) { char buf[64]; - /* expect+11: warning: field width '\101' (65) in 'f\000\101all+1\0' out of range 0..64 [372] */ - /* expect+10: warning: bit field end 65 in 'f\000\101all+1\0' out of range 0..64 [373] */ - /* expect+9: warning: bit field end 65 in 'f\001\100oob64\0' out of range 0..64 [373] */ + /* expect+12: warning: field width '\101' (65) in 'f\000\101all+1\0' out of range 0..64 [372] */ + /* expect+11: warning: bit field end 65 in 'f\000\101all+1\0' out of range 0..64 [373] */ + /* expect+10: warning: bit field end 65 in 'f\001\100oob64\0' out of range 0..64 [373] */ + /* expect+9: warning: 'f\001\100oob64\0' overlaps earlier 'f\000\100all\0' on bit 1 [376] */ /* expect+8: warning: field width '\377' (255) in 'f\010\377oob64\0' out of range 0..64 [372] */ /* expect+7: warning: bit field end 263 in 'f\010\377oob64\0' out of range 0..64 [373] */ snprintb(buf, sizeof(buf), Index: src/tests/usr.bin/xlint/lint1/msg_375.c diff -u src/tests/usr.bin/xlint/lint1/msg_375.c:1.1 src/tests/usr.bin/xlint/lint1/msg_375.c:1.2 --- src/tests/usr.bin/xlint/lint1/msg_375.c:1.1 Fri Mar 1 19:39:29 2024 +++ src/tests/usr.bin/xlint/lint1/msg_375.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: msg_375.c,v 1.1 2024/03/01 19:39:29 rillig Exp $ */ +/* $NetBSD: msg_375.c,v 1.2 2024/03/02 11:56:37 rillig Exp $ */ # 3 "msg_375.c" // Test for message: comparison value '%.*s' (%ju) exceeds field width %ju [375] @@ -30,7 +30,7 @@ example(uint64_t u64) "=\01715\0" "=\02016\0" "=\37716\0" - "F\000\004low\0" + "F\004\004low\0" ":\01715\0" ":\02016\0" ":\37716\0", Index: src/tests/usr.bin/xlint/lint1/t_usage.sh diff -u src/tests/usr.bin/xlint/lint1/t_usage.sh:1.16 src/tests/usr.bin/xlint/lint1/t_usage.sh:1.17 --- src/tests/usr.bin/xlint/lint1/t_usage.sh:1.16 Fri Mar 1 19:39:29 2024 +++ src/tests/usr.bin/xlint/lint1/t_usage.sh Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -# $NetBSD: t_usage.sh,v 1.16 2024/03/01 19:39:29 rillig Exp $ +# $NetBSD: t_usage.sh,v 1.17 2024/03/02 11:56:37 rillig Exp $ # # Copyright (c) 2023 The NetBSD Foundation, Inc. # All rights reserved. @@ -39,13 +39,13 @@ suppress_messages_body() # The largest known message. atf_check \ - "$lint1" -X 375 code.c /dev/null + "$lint1" -X 376 code.c /dev/null # Larger than the largest known message. atf_check \ -s 'exit:1' \ - -e "inline:lint1: invalid message ID '376'\n" \ - "$lint1" -X 376 code.c /dev/null + -e "inline:lint1: invalid message ID '377'\n" \ + "$lint1" -X 377 code.c /dev/null # Whitespace is not allowed before a message ID. atf_check \ Index: src/usr.bin/xlint/lint1/cksnprintb.c diff -u src/usr.bin/xlint/lint1/cksnprintb.c:1.2 src/usr.bin/xlint/lint1/cksnprintb.c:1.3 --- src/usr.bin/xlint/lint1/cksnprintb.c:1.2 Fri Mar 1 21:52:48 2024 +++ src/usr.bin/xlint/lint1/cksnprintb.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: cksnprintb.c,v 1.2 2024/03/01 21:52:48 rillig Exp $ */ +/* $NetBSD: cksnprintb.c,v 1.3 2024/03/02 11:56:37 rillig Exp $ */ /*- * Copyright (c) 2024 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ #include <sys/cdefs.h> #if defined(__RCSID) -__RCSID("$NetBSD: cksnprintb.c,v 1.2 2024/03/01 21:52:48 rillig Exp $"); +__RCSID("$NetBSD: cksnprintb.c,v 1.3 2024/03/02 11:56:37 rillig Exp $"); #endif #include <stdbool.h> @@ -43,6 +43,15 @@ __RCSID("$NetBSD: cksnprintb.c,v 1.2 202 #include "lint1.h" +typedef struct { + bool new_style; + const buffer *fmt; + uint64_t field_width; + uint64_t covered; + size_t covered_start[64]; + size_t covered_end[64]; +} checker; + static bool match_string_literal(const tnode_t *tn, const buffer **str) { @@ -120,9 +129,44 @@ check_hex_escape(const buffer *buf, quot warning(358, len(it), start(it, buf)); } +static void +check_overlap(checker *ck, uint64_t dir_lsb, uint64_t width, + size_t start, size_t end) +{ + if (dir_lsb >= 64 || width == 0 || width > 64) + return; + unsigned lsb = (unsigned)(ck->new_style ? dir_lsb : dir_lsb - 1); + + uint64_t field_mask = value_bits((unsigned)width) << lsb; + uint64_t overlap = ck->covered & field_mask; + if (overlap == 0) + goto done; + + for (unsigned i = lsb; i < 64; i++) { + if (!(overlap & bit(i))) + continue; + /* '%.*s' overlaps earlier '%.*s' on bit %u */ + warning(376, + (int)(end - start), ck->fmt->data + start, + (int)(ck->covered_end[i] - ck->covered_start[i]), + ck->fmt->data + ck->covered_start[i], + ck->new_style ? i : i + 1); + break; + } + +done: + ck->covered |= field_mask; + for (unsigned i = lsb; i < 64; i++) { + if (field_mask & bit(i)) { + ck->covered_start[i] = start; + ck->covered_end[i] = end; + } + } +} + static bool check_directive(const buffer *fmt, quoted_iterator *it, bool new_style, - uint64_t *prev_field_width) + checker *ck) { if (!quoted_next(fmt, it)) @@ -242,11 +286,15 @@ check_directive(const buffer *fmt, quote warning(373, val(bit) + val(width), range(dir, *it), start(dir, fmt)); } - if (has_cmp && *prev_field_width < 64 - && cmp.value & ~(uint64_t)0 << *prev_field_width) { + if (has_cmp && ck->field_width < 64 + && cmp.value & ~(uint64_t)0 << ck->field_width) { /* comparison value '%.*s' (%ju) exceeds field width %ju */ warning(375, len(cmp), start(cmp, fmt), val(cmp), - *prev_field_width); + (uintmax_t)ck->field_width); + } + if (has_bit) { + uint64_t w = has_width ? width.value : 1; + check_overlap(ck, bit.value, w, dir.start, it->i); } if (descr.i == prev.i && dir.value != 'F') { /* empty description in '%.*s' */ @@ -254,7 +302,7 @@ check_directive(const buffer *fmt, quote } if (has_width) - *prev_field_width = width.value; + ck->field_width = width.value; return true; } @@ -284,7 +332,11 @@ check_snprintb(const tnode_t *expr) return; } - uint64_t prev_field_width = 64; - while (check_directive(fmt, &it, new_style, &prev_field_width)) + checker ck = { + .new_style = new_style, + .fmt = fmt, + .field_width = 64, + }; + while (check_directive(fmt, &it, new_style, &ck)) continue; } Index: src/usr.bin/xlint/lint1/err.c diff -u src/usr.bin/xlint/lint1/err.c:1.227 src/usr.bin/xlint/lint1/err.c:1.228 --- src/usr.bin/xlint/lint1/err.c:1.227 Sat Mar 2 09:32:18 2024 +++ src/usr.bin/xlint/lint1/err.c Sat Mar 2 11:56:37 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: err.c,v 1.227 2024/03/02 09:32:18 rillig Exp $ */ +/* $NetBSD: err.c,v 1.228 2024/03/02 11:56:37 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -37,7 +37,7 @@ #include <sys/cdefs.h> #if defined(__RCSID) -__RCSID("$NetBSD: err.c,v 1.227 2024/03/02 09:32:18 rillig Exp $"); +__RCSID("$NetBSD: err.c,v 1.228 2024/03/02 11:56:37 rillig Exp $"); #endif #include <limits.h> @@ -431,6 +431,7 @@ static const char *const msgs[] = { "bit field end %ju in '%.*s' out of range 0..64", // 373 "unknown directive '%.*s'", // 374 "comparison value '%.*s' (%ju) exceeds field width %ju", // 375 + "'%.*s' overlaps earlier '%.*s' on bit %u", // 376 }; static bool is_suppressed[sizeof(msgs) / sizeof(msgs[0])]; Added files: Index: src/tests/usr.bin/xlint/lint1/msg_376.c diff -u /dev/null src/tests/usr.bin/xlint/lint1/msg_376.c:1.1 --- /dev/null Sat Mar 2 11:56:37 2024 +++ src/tests/usr.bin/xlint/lint1/msg_376.c Sat Mar 2 11:56:37 2024 @@ -0,0 +1,56 @@ +/* $NetBSD: msg_376.c,v 1.1 2024/03/02 11:56:37 rillig Exp $ */ +# 3 "msg_376.c" + +// Test for message: '%.*s' overlaps earlier '%.*s' on bit %u [376] + +/* + * When bits and fields overlap, it's often due to typos or off-by-one errors. + */ + +/* lint1-extra-flags: -X 351 */ + +typedef typeof(sizeof(0)) size_t; +typedef unsigned long long uint64_t; + +int snprintb(char*, size_t, const char*, uint64_t); + +void +example(unsigned u32, uint64_t u64) +{ + char buf[64]; + + // In the old-style format, bit positions are 1-based. + /* expect+10: warning: '\x01lsb' overlaps earlier '\001lsb' on bit 1 [376] */ + /* expect+9: warning: '\x20msb""\041oob""\x21oob' overlaps earlier '\040msb' on bit 32 [376] */ + snprintb(buf, sizeof(buf), + "\020" + "\001lsb" + "\x01lsb" + "\040msb" + "\x20msb" + "\041oob" + "\x21oob", + u32); + + // In the new-style format, bit positions are 1-based. + /* expect+10: warning: 'b\x00lsb\0' overlaps earlier 'b\000lsb\0' on bit 0 [376] */ + /* expect+9: warning: 'b\x3fmsb\0' overlaps earlier 'b\077msb\0' on bit 63 [376] */ + /* expect+8: warning: bit position '\x40' (64) in 'b\x40oob\0' out of range 0..63 [371] */ + snprintb(buf, sizeof(buf), + "\177\020" + "b\000lsb\0" + "b\x00lsb\0" + "b\077msb\0" + "b\x3fmsb\0" + "b\x40oob\0", + u64); + + /* expect+7: warning: 'F\014\010f2\0' overlaps earlier 'f\010\010f1\0' on bit 12 [376] */ + /* expect+6: warning: 'f\020\010f3\0' overlaps earlier 'F\014\010f2\0' on bit 16 [376] */ + snprintb(buf, sizeof(buf), + "\177\020" + "f\010\010f1\0" + "F\014\010f2\0" + "f\020\010f3\0", + u64); +}