1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
/* $NetBSD: msg_168.c,v 1.10 2023/03/28 14:44:35 rillig Exp $ */
# 3 "msg_168.c"
// Test for message: array subscript cannot be > %d: %ld [168]
/* lint1-extra-flags: -X 351 */
void print_string(const char *);
void print_char(char);
void
example(void)
{
char buf[20] = {}; /* empty initializer is a GCC extension */
print_string(buf + 19); /* inside the array */
/*
* It is valid to point at the end of the array, but reading a
* character from there invokes undefined behavior.
*
* The pointer to the end of the array is typically used in (begin,
* end) tuples. These are more common in C++ than in C though.
*/
print_string(buf + 20);
print_string(buf + 21); /* undefined behavior, not detected */
print_char(buf[19]);
/* expect+1: warning: array subscript cannot be > 19: 20 [168] */
print_char(buf[20]);
}
void
array_with_c99_initializer(void)
{
static const char *const to_roman[] = {
['0'] = "undefined",
['5'] = "V",
['9'] = "IX"
};
print_string(to_roman['9']);
/* expect+1: warning: array subscript cannot be > 57: 58 [168] */
print_string(to_roman[':']);
}
/*
* In its expression tree, lint represents pointer addition as 'ptr + off',
* where 'off' is the offset in bytes, regardless of the pointer type.
*
* In the below code, the member 'offset_8' has type 'short', and the
* expression 's->offset_8' is represented as '&s + 8', or more verbose:
*
* '+' type 'pointer to short'
* '&' type 'pointer to struct s'
* 'name' 's' with auto 'array[1] of struct s', lvalue
* 'constant' type 'long', value 8
*
* The constant 8 differs from the usual model of pointer arithmetics. Since
* the type of the '&' expression is 'pointer to struct s', adding a constant
* would rather be interpreted as adding 'constant * sizeof(struct s)', and
* to access a member, the pointer to 'struct s' would need to be converted
* to 'pointer of byte' first, then adding the offset 8, then converting the
* pointer to the target type 'pointer to short'.
*
* Lint uses the simpler representation, saving a few conversions on the way.
* Without this pre-multiplied representation, the below code would generate
* warnings about out-of-bounds array access, starting with offset_1.
*/
struct s {
char offset_0;
char offset_1;
int offset_4;
short offset_8;
char offset_10;
};
struct s
s_init(void)
{
struct s s[1];
s->offset_0 = 1;
s->offset_1 = 2;
s->offset_4 = 3;
s->offset_8 = 4;
s->offset_10 = 5;
return s[0];
}
|