Bug Summary

File:d/tclparsefieldvalue.c
Warning:line 93, column 5
Duplicate code detected
Note:line 163, column 9
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tclparsefieldvalue.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c tclparsefieldvalue.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30/*
31 * fieldvalue.c --
32 *
33 * Routines to parse the content of request/replay header fields.
34 *
35 * RFC7230: https://tools.ietf.org/html/rfc7230#section-3.2.6
36 *
37 * Example:
38 * RFC7239: https://tools.ietf.org/html/rfc7239#section-4
39 *
40 */
41#include "nsd.h"
42
43/*
44 * Local functions defined in this file.
45 */
46
47static const unsigned char *
48GetToken(Tcl_DString *dsPtr, const unsigned char *source)
49 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
50
51static const unsigned char *
52GetQuotedString(Tcl_DString *dsPtr, const unsigned char *source)
53 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
54
55static const unsigned char *
56SkipWhitespace(const unsigned char *source)
57 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
58
59/*
60 *----------------------------------------------------------------------
61 *
62 * GetToken --
63 *
64 * Read a token as defined in rfc7230#section-3.2.6 from source and add
65 * it to the provided Tcl_DString.
66 *
67 * Results:
68 * Pointer to the next character to parse. In case, the source
69 * pointer is the same as the result, no token was parsed.
70 *
71 * Side effects:
72 * None.
73 *
74 *----------------------------------------------------------------------
75 */
76static const unsigned char *
77GetToken(Tcl_DString *dsPtr, const unsigned char *source)
78{
79 register const unsigned char *p;
80 /*
81 * RFC 7230 https://tools.ietf.org/html/rfc7230#section-3.2.6
82 * defines a token as
83 *
84 * token = 1*tchar
85 * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
86 * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
87 * / DIGIT / ALPHA
88 * ; any VCHAR, except delimiters
89 *
90 * ; DIGIT %x30-39
91 * ; ALPHA %x41-5A / %x61-7A
92 */
93 static const bool_Bool token_char[] = {
Duplicate code detected
94 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
95 /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97 /* 0x20 */ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
98 /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
99 /* 0x40 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
100 /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
101 /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
102 /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
103 /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104 /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105 /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106 /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107 /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
111 };
112
113 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
114 NS_NONNULL_ASSERT(source != NULL)((void) (0));
115
116 for (p = source; token_char[*p]; p++) {
117 /*
118 * When the string is properly preallocated, the following
119 * statement can be simplified
120 */
121 Tcl_DStringAppend(dsPtr, (const char *)p, 1);
122 }
123
124 return p;
125}
126
127/*
128 *----------------------------------------------------------------------
129 *
130 * GetQuotedString --
131 *
132 * Read a quoted string as defined in rfc7230#section-3.2.6 from source
133 * and add it to the provided Tcl_DString.
134 *
135 * Results:
136 * Pointer to the next character to parse. In case, the source pointer
137 * is the same result, no quoted string could be parsed.
138 *
139 * Side effects:
140 * None.
141 *
142 *----------------------------------------------------------------------
143 */
144const unsigned char *
145GetQuotedString(Tcl_DString *dsPtr, const unsigned char *source)
146{
147 register const unsigned char *p;
148
149 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
150 NS_NONNULL_ASSERT(source != NULL)((void) (0));
151
152 /*
153 * RFC 7230 https://tools.ietf.org/html/rfc7230#section-3.2.6
154 * defines a quoted string as
155 *
156 * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
157 * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
158 * obs-text = %x80-FF
159 */
160
161 p = source;
162 if (*p == '"') {
163 static const bool_Bool qdtext_char[] = {
Similar code here
164 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
165 /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
166 /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 /* 0x20 */ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
168 /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
169 /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
170 /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
171 /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
172 /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
173 /* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
174 /* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
175 /* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
176 /* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 /* 0xc0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178 /* 0xd0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 /* 0xe0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
180 /* 0xf0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
181 };
182 bool_Bool qpair = NS_FALSE0;
183
184 for (p++; ; p++) {
185 if (qdtext_char[*p]) {
186 Tcl_DStringAppend(dsPtr, (const char *)p, 1);
187 } else if (!qpair && *p == '\\') {
188 qpair = NS_TRUE1;
189 } else if (qpair && (*p == 0x09 || *p>10)) {
190 qpair = NS_FALSE0;
191 Tcl_DStringAppend(dsPtr, (const char *)p, 1);
192 } else if (*p == '"') {
193 p++;
194 break;
195 } else {
196 Ns_Log(Warning, "Unexpected character %c in header field <%s>",
197 *p, (char *)source);
198 break;
199 }
200 }
201 }
202 return p;
203}
204
205/*
206 *----------------------------------------------------------------------
207 *
208 * SkipWhitespace --
209 *
210 * Skip pver whitespace in the provided input string.
211 *
212 * Results:
213 * Pointer to the character after the white space. In case, the source
214 * pointer is the same as the result, no white space was skipped.
215 *
216 * Side effects:
217 * None.
218 *
219 *----------------------------------------------------------------------
220 */
221static const unsigned char *
222SkipWhitespace(const unsigned char *source) {
223 NS_NONNULL_ASSERT(source != NULL)((void) (0));
224
225 for (; (CHARTYPE(space, *source)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*source))
))] & (unsigned short int) _ISspace))
!= 0); source++) {
226 }
227 return source;
228}
229
230/*
231 *----------------------------------------------------------------------
232 *
233 * NsTclParseFieldvalue --
234 *
235 * Parse one or multiple token value pairse from the content of the
236 * request header fields. These elements have typically the form:
237 *
238 * elements = element *( OWS "," OWS element )
239 * element = [ pair ] *( ";" [ pair ] )
240 * pair = token "=" value
241 * value = token / quoted-string
242 *
243 * Per default this function returns a Tcl list of elements, where every
244 * element has the form of a Tcl dict, containing values from the "pair"
245 * definitions. When the option "-single" is specified, the first
246 * "element" is parsed and returned as a single dict.
247 *
248 * Implements "ns_parsefieldvalue".
249 *
250 * Results:
251 * Tcl result.
252 *
253 * Side effects:
254 * None.
255 *
256 *----------------------------------------------------------------------
257 */
258int
259NsTclParseFieldvalue(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp,
260 int objc, Tcl_Obj *const* objv)
261{
262 int result = TCL_OK0, singleInt = (int)NS_FALSE0,
263 lowerInt = (int)NS_FALSE0, strictInt = (int)NS_FALSE0;
264 char *sourceString;
265 Ns_ObjvSpec opts[] = {
266 {"-lower", Ns_ObjvBool, &lowerInt, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
267 {"-single", Ns_ObjvBool, &singleInt, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
268 {"-strict", Ns_ObjvBool, &strictInt, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
269 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
270 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
271 };
272 Ns_ObjvSpec args[] = {
273 {"fieldvalue", Ns_ObjvString, &sourceString, NULL((void*)0)},
274 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
275 };
276
277 if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) {
278 result = TCL_ERROR1;
279
280 } else {
281 Ns_DStringTcl_DString token, value;
282 const unsigned char *p1;
283 const char listDelimiter = (singleInt == (int)NS_TRUE1 ? '\0' : ',');
284 const char sublistDelimiter = ';';
285 Tcl_Obj *listObj = NULL((void*)0), *sublistObj = NULL((void*)0);
286
287 Ns_DStringInitTcl_DStringInit(&token);
288 Ns_DStringInitTcl_DStringInit(&value);
289 p1 = SkipWhitespace((unsigned char *)sourceString);
290
291 for (;;) {
292 const unsigned char *p2;
293
294 p1 = SkipWhitespace(p1);
295 p2 = GetToken(&token, p1);
296 if (p1 == p2) {
297 /*
298 * Silently skip tokens without names.
299 */
300 p1 ++;
301 continue;
302 }
303
304 p1 = SkipWhitespace(p2);
305 if (*p1 == '=') {
306 p1 ++;
307 p1 = SkipWhitespace(p1);
308 p2 = GetToken(&value, p1);
309 if (p1 == p2) {
310 p2 = GetQuotedString(&value, p1);
311 }
312 p1 = SkipWhitespace(p2);
313 }
314
315 if (sublistObj == NULL((void*)0)) {
316 sublistObj = Tcl_NewListObj(0, NULL((void*)0));
317 }
318 if (lowerInt == (int)NS_TRUE1) {
319 Ns_StrToLower(token.string);
320 }
321 Tcl_ListObjAppendElement(interp, sublistObj, Tcl_NewStringObj(token.string, token.length));
322 Tcl_ListObjAppendElement(interp, sublistObj, Tcl_NewStringObj(value.string, value.length));
323 Ns_DStringSetLengthTcl_DStringSetLength(&token, 0);
324 Ns_DStringSetLengthTcl_DStringSetLength(&value, 0);
325
326 if (*p1 == sublistDelimiter) {
327 p1 ++;
328 /*
329 * Continue with sublist.
330 */
331 } else if (*p1 != '\0' && *p1 == listDelimiter) {
332 p1 ++;
333 if (listObj == NULL((void*)0)) {
334 listObj = Tcl_NewListObj(0, NULL((void*)0));
335 }
336 Tcl_ListObjAppendElement(interp, listObj, sublistObj);
337 sublistObj = NULL((void*)0);
338 /*
339 * Continue with list.
340 */
341 } else {
342 break;
343 }
344 }
345 if (listObj != NULL((void*)0)) {
346 /*
347 * We have a list; if there is an unhandled sublist, append it.
348 */
349 if (sublistObj != NULL((void*)0)) {
350 Tcl_ListObjAppendElement(interp, listObj, sublistObj);
351 sublistObj = NULL((void*)0);
352 }
353 } else if (sublistObj != NULL((void*)0)) {
354 /*
355 * We have just a sublist, but no list. In "-single" mode, return
356 * just the dict, otherwise return always a list of dicts.
357 */
358 if (singleInt == (int)NS_TRUE1) {
359 listObj = sublistObj;
360 } else {
361 listObj = Tcl_NewListObj(0, NULL((void*)0));
362 Tcl_ListObjAppendElement(interp, listObj, sublistObj);
363 }
364 }
365 if (strictInt == (int)NS_TRUE1 && *p1 != '\0') {
366 Ns_TclPrintfResult(interp, "unparsed content '%s'", p1);
367 result = TCL_ERROR1;
368 if (listObj != NULL((void*)0)) {
369 Tcl_DecrRefCount(listObj)do { Tcl_Obj *_objPtr = (listObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
370 }
371
372 } else if (listObj != NULL((void*)0)) {
373 Tcl_SetObjResult(interp, listObj);
374 }
375 Ns_DStringFreeTcl_DStringFree(&token);
376 Ns_DStringFreeTcl_DStringFree(&value);
377 }
378
379 return result;
380}
381
382/*
383 * Local Variables:
384 * mode: c
385 * c-basic-offset: 4
386 * fill-column: 78
387 * indent-tabs-mode: nil
388 * End:
389 */