msg_247.c revision 1.24 1 /* $NetBSD: msg_247.c,v 1.24 2022/06/24 20:44:53 rillig Exp $ */
2 # 3 "msg_247.c"
3
4 // Test for message: pointer cast from '%s' to '%s' may be troublesome [247]
5
6 /* lint1-extra-flags: -c */
7
8 /* example taken from Xlib.h */
9 typedef struct {
10 int id;
11 } *PDisplay;
12
13 struct Other {
14 int id;
15 };
16
17 PDisplay
18 example(struct Other *arg)
19 {
20 /*
21 * Before tree.c 1.461 from 2022-06-24, lint warned about the cast
22 * between the structs.
23 *
24 * XXX: The target type was reported as 'struct <unnamed>'. In cases
25 * like these, it would be helpful to print at least the type name
26 * of the pointer. This type name though is discarded immediately
27 * in the grammar rule 'typespec: T_TYPENAME'.
28 * After that, the target type of the cast is just an unnamed struct,
29 * with no hint at all that there is a typedef for a pointer to the
30 * struct.
31 */
32 return (PDisplay)arg;
33 }
34
35 /*
36 * C code with a long history that has existed in pre-C90 times already often
37 * uses 'pointer to char' where modern code would use 'pointer to void'.
38 * Since 'char' is the most general underlying type, there is nothing wrong
39 * with casting to it. An example for this type of code is X11.
40 *
41 * Casting to 'pointer to char' may also be used by programmers who don't know
42 * about endianness, but that's not something lint can do anything about. The
43 * code for these two use cases looks exactly the same, so lint errs on the
44 * side of fewer false positive warnings here.
45 */
46 char *
47 cast_to_char_pointer(struct Other *arg)
48 {
49 return (char *)arg;
50 }
51
52 /*
53 * In traditional C there was 'unsigned char' as well, so the same reasoning
54 * as for plain 'char' applies here.
55 */
56 unsigned char *
57 cast_to_unsigned_char_pointer(struct Other *arg)
58 {
59 return (unsigned char *)arg;
60 }
61
62 /*
63 * Traditional C does not have the type specifier 'signed', which means that
64 * this type cannot be used by old code. Therefore warn about this. All code
65 * that triggers this warning should do the intermediate cast via 'void
66 * pointer'.
67 */
68 signed char *
69 cast_to_signed_char_pointer(struct Other *arg)
70 {
71 /* expect+1: warning: pointer cast from 'pointer to struct Other' to 'pointer to signed char' may be troublesome [247] */
72 return (signed char *)arg;
73 }
74
75 char *
76 cast_to_void_pointer_then_to_char_pointer(struct Other *arg)
77 {
78 return (char *)(void *)arg;
79 }
80
81
82 /*
83 * When implementing types that have a public part that is exposed to the user
84 * (in this case 'struct counter') and a private part that is only visible to
85 * the implementation (in this case 'struct counter_impl'), a common
86 * implementation technique is to use a struct in which the public part is the
87 * first member. C guarantees that the pointer to the first member is at the
88 * same address as the pointer to the whole struct.
89 *
90 * Seen in external/mpl/bind/dist/lib/isc/mem.c for 'struct isc_mem' and
91 * 'struct isc__mem'.
92 */
93
94 struct counter {
95 int count;
96 };
97
98 struct counter_impl {
99 struct counter public_part;
100 int saved_count;
101 };
102
103 void *allocate(void);
104
105 struct counter *
106 counter_new_typesafe(void)
107 {
108 struct counter_impl *impl = allocate();
109 impl->public_part.count = 12345;
110 impl->saved_count = 12346;
111 return &impl->public_part;
112 }
113
114 struct counter *
115 counter_new_cast(void)
116 {
117 struct counter_impl *impl = allocate();
118 impl->public_part.count = 12345;
119 impl->saved_count = 12346;
120 /* Before tree.c 1.462 from 2022-06-24, lint warned about this cast. */
121 return (struct counter *)impl;
122 }
123
124 void
125 counter_increment(struct counter *counter)
126 {
127 /*
128 * Before tree.c 1.272 from 2021-04-08, lint warned about the cast
129 * from 'struct counter' to 'struct counter_impl'.
130 */
131 struct counter_impl *impl = (struct counter_impl *)counter;
132 impl->saved_count = impl->public_part.count;
133 impl->public_part.count++;
134 }
135
136
137 /*
138 * In OpenSSL, the hashing API uses the incomplete 'struct lhash_st' for their
139 * type-generic hashing API while defining a separate struct for each type to
140 * be hashed.
141 *
142 * Before 2021-04-09, in a typical NetBSD build this led to about 38,000 lint
143 * warnings about possibly troublesome pointer casts.
144 */
145
146 /* expect+1: warning: struct 'lhash_st' never defined [233] */
147 struct lhash_st;
148
149 struct lhash_st *OPENSSL_LH_new(void);
150
151 struct lhash_st_OPENSSL_STRING {
152 union lh_OPENSSL_STRING_dummy {
153 void *d1;
154 unsigned long d2;
155 int d3;
156 } dummy;
157 };
158
159 # 196 "lhash.h" 1 3 4
160 struct lhash_st_OPENSSL_STRING *
161 lh_OPENSSL_STRING_new(void)
162 {
163 /*
164 * Since tree.c 1.274 from 2021-04-09, lint does not warn about casts
165 * to or from incomplete structs anymore.
166 */
167 return (struct lhash_st_OPENSSL_STRING *)OPENSSL_LH_new();
168 }
169 # 170 "msg_247.c" 2
170
171 void sink(const void *);
172
173 /*
174 * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from
175 * unsigned char or plain char to another type. These casts often occur in
176 * traditional code that does not use void pointers, even 30 years after C90
177 * introduced 'void'.
178 */
179 void
180 unsigned_char_to_unsigned_type(unsigned char *ucp)
181 {
182 unsigned short *usp;
183
184 usp = (unsigned short *)ucp;
185 sink(usp);
186 }
187
188 /*
189 * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from
190 * unsigned char or plain char to another type. These casts often occur in
191 * traditional code that does not use void pointers, even 30 years after C90
192 * introduced 'void'.
193 */
194 void
195 plain_char_to_unsigned_type(char *cp)
196 {
197 unsigned short *usp;
198
199 usp = (unsigned short *)cp;
200 sink(usp);
201 }
202
203 /*
204 * Before tree.c 1.460 from 2022-06-24, lint warned about pointer casts from
205 * unsigned char or plain char to a struct or union type. These casts often
206 * occur in traditional code that does not use void pointers, even 30 years
207 * after C90 introduced 'void'.
208 */
209 void
210 char_to_struct(void *ptr)
211 {
212
213 sink((struct counter *)(char *)ptr);
214
215 sink((struct counter *)(unsigned char *)ptr);
216
217 /* expect+1: warning: pointer cast from 'pointer to signed char' to 'pointer to struct counter' may be troublesome [247] */
218 sink((struct counter *)(signed char *)ptr);
219 }
220
221
222 // The following data types are simplified from various system headers.
223
224 typedef unsigned char uint8_t;
225 typedef unsigned short uint16_t;
226 typedef unsigned int uint32_t;
227
228 typedef uint16_t in_port_t;
229 typedef uint8_t sa_family_t;
230
231 struct sockaddr {
232 uint8_t sa_len;
233 sa_family_t sa_family;
234 char sa_data[14];
235 };
236
237 struct in_addr {
238 uint32_t s_addr;
239 };
240
241 struct sockaddr_in {
242 uint8_t sin_len;
243 sa_family_t sin_family;
244 in_port_t sin_port;
245 struct in_addr sin_addr;
246 uint8_t sin_zero[8];
247 };
248
249 struct sockaddr_in6 {
250 uint8_t sin6_len;
251 sa_family_t sin6_family;
252 in_port_t sin6_port;
253 uint32_t sin6_flowinfo;
254 union {
255 uint8_t u6_addr8[16];
256 uint16_t u6_addr16[8];
257 uint32_t u6_addr32[4];
258 } sin6_addr;
259 uint32_t sin6_scope_id;
260 };
261
262 /*
263 * Before tree.c 1.461 from 2022-06-24, lint warned about the cast between the
264 * sockaddr variants. Since then, lint allows casts between pointers to
265 * structs if the initial members have compatible types and either of the
266 * struct types continues with a byte array.
267 */
268 void *
269 cast_between_sockaddr_variants(void *ptr)
270 {
271
272 void *t1 = (struct sockaddr_in *)(struct sockaddr *)ptr;
273 void *t2 = (struct sockaddr *)(struct sockaddr_in *)t1;
274 void *t3 = (struct sockaddr_in6 *)(struct sockaddr *)t2;
275 void *t4 = (struct sockaddr *)(struct sockaddr_in6 *)t3;
276
277 /* expect+1: warning: pointer cast from 'pointer to struct sockaddr_in6' to 'pointer to struct sockaddr_in' may be troublesome [247] */
278 void *t5 = (struct sockaddr_in *)(struct sockaddr_in6 *)t4;
279
280 /* expect+1: warning: pointer cast from 'pointer to struct sockaddr_in' to 'pointer to struct sockaddr_in6' may be troublesome [247] */
281 void *t6 = (struct sockaddr_in6 *)(struct sockaddr_in *)t5;
282
283 return t6;
284 }
285
286
287 // From jemalloc.
288
289 typedef struct ctl_node_s {
290 _Bool named;
291 } ctl_node_t;
292
293 typedef struct ctl_named_node_s {
294 ctl_node_t node;
295 const char *name;
296 } ctl_named_node_t;
297
298 void *
299 cast_between_first_member_struct(void *ptr)
300 {
301 /* Before tree.c 1.462 from 2022-06-24, lint warned about this cast. */
302 void *t1 = (ctl_node_t *)(ctl_named_node_t *)ptr;
303
304 void *t2 = (ctl_named_node_t *)(ctl_node_t *)ptr;
305
306 return t2;
307 }
308