Skip to content

Commit a25d121

Browse files
authored
Resolve all MISRA violations (#19)
1 parent dead57c commit a25d121

File tree

6 files changed

+144
-63
lines changed

6 files changed

+144
-63
lines changed

lexicon.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ bf
55
bmp
66
buf
77
bufferlength
8+
cbmc
89
com
910
cond
1011
corejson
@@ -18,6 +19,7 @@ ef
1819
endcode
1920
endcond
2021
endif
22+
enum
2123
enums
2224
fb
2325
fc
@@ -36,6 +38,7 @@ jsonmaxdepthexceeded
3638
jsonnotfound
3739
jsonnullparameter
3840
jsonpartial
41+
jsonstatus
3942
jsonsuccess
4043
keylength
4144
len

source/core_json.c

Lines changed: 104 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,19 @@ typedef enum
3636
false = 0
3737
} bool_;
3838

39-
#define isdigit_( x ) ( ( x >= '0' ) && ( x <= '9' ) )
40-
#define iscntrl_( x ) ( ( x >= '\0' ) && ( x < ' ' ) )
39+
/* A compromise to satisfy both MISRA and CBMC */
40+
typedef union
41+
{
42+
char c;
43+
uint8_t u;
44+
} char_;
45+
46+
#define isdigit_( x ) ( ( ( x ) >= '0' ) && ( ( x ) <= '9' ) )
47+
#define iscntrl_( x ) ( ( ( x ) >= '\0' ) && ( ( x ) < ' ' ) )
4148
/* NB. This is whitespace as defined by the JSON standard (ECMA-404). */
42-
#define isspace_( x ) ( ( x == ' ' ) || ( x == '\t' ) || ( x == '\n' ) || ( x == '\r' ) )
49+
#define isspace_( x ) \
50+
( ( ( x ) == ' ' ) || ( ( x ) == '\t' ) || \
51+
( ( x ) == '\n' ) || ( ( x ) == '\r' ) )
4352

4453
/**
4554
* @brief Advance buffer index beyond whitespace.
@@ -112,7 +121,7 @@ static bool_ shortestUTF8( size_t length,
112121
bool_ ret = false;
113122
uint32_t min, max;
114123

115-
assert( ( length >= 2 ) && ( length <= 4 ) );
124+
assert( ( length >= 2U ) && ( length <= 4U ) );
116125

117126
switch( length )
118127
{
@@ -171,20 +180,20 @@ static bool_ skipUTF8MultiByte( const char * buf,
171180
bool_ ret = false;
172181
size_t i, bitCount, j;
173182
uint32_t value = 0;
174-
uint8_t c;
183+
char_ c;
175184

176185
assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
177186

178187
i = *start;
179188
assert( i < max );
180-
assert( buf[ i ] < 0 );
189+
assert( buf[ i ] < '\0' );
181190

182-
c = ( 0x80U | ( buf[ i ] & 0x7F ) );
191+
c.c = buf[ i ];
183192

184-
if( ( c > 0xC1U ) && ( c < 0xF5U ) )
193+
if( ( c.u > 0xC1U ) && ( c.u < 0xF5U ) )
185194
{
186-
bitCount = countHighBits( c );
187-
value = ( ( uint32_t ) c ) & ( ( ( uint32_t ) 1 << ( 7U - bitCount ) ) - 1U );
195+
bitCount = countHighBits( c.u );
196+
value = ( ( uint32_t ) c.u ) & ( ( ( uint32_t ) 1 << ( 7U - bitCount ) ) - 1U );
188197

189198
/* The bit count is 1 greater than the number of bytes,
190199
* e.g., when j is 2, we skip one more byte. */
@@ -197,13 +206,15 @@ static bool_ skipUTF8MultiByte( const char * buf,
197206
break;
198207
}
199208

209+
c.c = buf[ i ];
210+
200211
/* Additional bytes must match 10xxxxxx. */
201-
if( ( buf[ i ] >= 0 ) || ( ( buf[ i ] & 0x40 ) != 0 ) )
212+
if( ( c.u & 0xC0U ) != 0x80U )
202213
{
203214
break;
204215
}
205216

206-
value = ( value << 6U ) | ( buf[ i ] & 0x3F );
217+
value = ( value << 6U ) | ( c.u & 0x3FU );
207218
}
208219

209220
if( ( j == 0U ) && ( shortestUTF8( bitCount, value ) == true ) )
@@ -237,7 +248,7 @@ static bool_ skipUTF8( const char * buf,
237248
if( *start < max )
238249
{
239250
/* an ASCII byte */
240-
if( buf[ *start ] >= 0 )
251+
if( buf[ *start ] >= '\0' )
241252
{
242253
*start += 1U;
243254
ret = true;
@@ -256,69 +267,62 @@ static bool_ skipUTF8( const char * buf,
256267
*
257268
* @param[in] c The character to convert.
258269
*
259-
* @return the integer value upon success or UINT8_MAX on failure.
270+
* @return the integer value upon success or NOT_A_HEX_CHAR on failure.
260271
*/
272+
#define NOT_A_HEX_CHAR ( 0x10U )
261273
static uint8_t hexToInt( char c )
262274
{
263-
uint8_t n;
275+
char_ n;
276+
277+
n.c = c;
264278

265279
if( ( c >= 'a' ) && ( c <= 'f' ) )
266280
{
267-
n = 10U + ( uint8_t ) ( c - 'a' );
281+
n.c -= 'a';
282+
n.u += 10U;
268283
}
269284
else if( ( c >= 'A' ) && ( c <= 'F' ) )
270285
{
271-
n = 10U + ( uint8_t ) ( c - 'A' );
286+
n.c -= 'A';
287+
n.u += 10U;
272288
}
273289
else if( isdigit_( c ) )
274290
{
275-
n = ( uint8_t ) ( c - '0' );
291+
n.c -= '0';
276292
}
277293
else
278294
{
279-
n = UINT8_MAX;
295+
n.u = NOT_A_HEX_CHAR;
280296
}
281297

282-
return n;
298+
return n.u;
283299
}
284300

285301
/**
286-
* @brief Advance buffer index beyond a \u Unicode escape sequence.
302+
* @brief Advance buffer index beyond a single \u Unicode
303+
* escape sequence and output the value.
287304
*
288305
* @param[in] buf The buffer to parse.
289306
* @param[in,out] start The index at which to begin.
290307
* @param[in] max The size of the buffer.
291-
* @param[in] requireLowSurrogate true when a low surrogate is required.
292-
*
293-
* Surrogate pairs are two escape sequences that together denote
294-
* a code point outside the Basic Multilingual Plane. They must
295-
* occur as a pair with the first "high" value in [U+D800, U+DBFF],
296-
* and the second "low" value in [U+DC00, U+DFFF].
308+
* @param[out] outValue The value of the hex digits.
297309
*
298310
* @return true if a valid escape sequence was present;
299311
* false otherwise.
300312
*
301313
* @note For the sake of security, \u0000 is disallowed.
302314
*/
303-
#define isHighSurrogate( x ) ( ( ( x ) >= 0xD800U ) && ( ( x ) <= 0xDBFFU ) )
304-
#define isLowSurrogate( x ) ( ( ( x ) >= 0xDC00U ) && ( ( x ) <= 0xDFFFU ) )
305-
306-
/* MISRA Rule 17.2 prohibits recursion due to the
307-
* risk of exceeding available stack space. In this
308-
* function, recursion is limited to exactly one level;
309-
* the recursive call sets the final argument to true
310-
* which satisfies the base case. */
311-
/* coverity[misra_c_2012_rule_17_2_violation] */
312-
static bool_ skipHexEscape( const char * buf,
313-
size_t * start,
314-
size_t max,
315-
bool_ requireLowSurrogate )
315+
static bool_ skipOneHexEscape( const char * buf,
316+
size_t * start,
317+
size_t max,
318+
uint16_t * outValue )
316319
{
317320
bool_ ret = false;
318321
size_t i, end;
319322
uint16_t value = 0;
320323

321324
assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
325+
assert( outValue != NULL );
322326

323327
i = *start;
324328
#define HEX_ESCAPE_LENGTH ( 6U ) /* e.g., \u1234 */
@@ -330,37 +334,75 @@ static bool_ skipHexEscape( const char * buf,
330334
{
331335
uint8_t n = hexToInt( buf[ i ] );
332336

333-
if( n == UINT8_MAX )
337+
if( n == NOT_A_HEX_CHAR )
334338
{
335339
break;
336340
}
337341

338342
value = ( value << 4U ) | n;
339343
}
344+
}
340345

341-
if( ( i == end ) && ( value > 0U ) )
346+
if( ( i == end ) && ( value > 0U ) )
347+
{
348+
ret = true;
349+
*outValue = value;
350+
*start = i;
351+
}
352+
353+
return ret;
354+
}
355+
356+
/**
357+
* @brief Advance buffer index beyond one or a pair of \u Unicode escape sequences.
358+
*
359+
* @param[in] buf The buffer to parse.
360+
* @param[in,out] start The index at which to begin.
361+
* @param[in] max The size of the buffer.
362+
*
363+
* Surrogate pairs are two escape sequences that together denote
364+
* a code point outside the Basic Multilingual Plane. They must
365+
* occur as a pair with the first "high" value in [U+D800, U+DBFF],
366+
* and the second "low" value in [U+DC00, U+DFFF].
367+
*
368+
* @return true if a valid escape sequence was present;
369+
* false otherwise.
370+
*
371+
* @note For the sake of security, \u0000 is disallowed.
372+
*/
373+
#define isHighSurrogate( x ) ( ( ( x ) >= 0xD800U ) && ( ( x ) <= 0xDBFFU ) )
374+
#define isLowSurrogate( x ) ( ( ( x ) >= 0xDC00U ) && ( ( x ) <= 0xDFFFU ) )
375+
376+
static bool_ skipHexEscape( const char * buf,
377+
size_t * start,
378+
size_t max )
379+
{
380+
bool_ ret = false;
381+
size_t i;
382+
uint16_t value;
383+
384+
assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
385+
386+
i = *start;
387+
388+
if( skipOneHexEscape( buf, &i, max, &value ) == true )
389+
{
390+
if( isHighSurrogate( value ) )
342391
{
343-
if( requireLowSurrogate == true )
344-
{
345-
if( isLowSurrogate( value ) )
346-
{
347-
ret = true;
348-
}
349-
}
350-
else if( isHighSurrogate( value ) )
351-
{
352-
/* low surrogate must follow */
353-
ret = skipHexEscape( buf, &i, max, true );
354-
}
355-
else if( isLowSurrogate( value ) )
356-
{
357-
/* premature low surrogate */
358-
}
359-
else
392+
if( ( skipOneHexEscape( buf, &i, max, &value ) == true ) &&
393+
( isLowSurrogate( value ) ) )
360394
{
361395
ret = true;
362396
}
363397
}
398+
else if( isLowSurrogate( value ) )
399+
{
400+
/* premature low surrogate */
401+
}
402+
else
403+
{
404+
ret = true;
405+
}
364406
}
365407

366408
if( ret == true )
@@ -404,7 +446,7 @@ static bool_ skipEscape( const char * buf,
404446
break;
405447

406448
case 'u':
407-
ret = skipHexEscape( buf, &i, max, false );
449+
ret = skipHexEscape( buf, &i, max );
408450
break;
409451

410452
case '"':
@@ -587,7 +629,7 @@ static bool_ skipAnyLiteral( const char * buf,
587629
bool_ ret = false;
588630

589631
#define skipLit_( x ) \
590-
( skipLiteral( buf, start, max, x, ( sizeof( x ) - 1U ) ) == true )
632+
( skipLiteral( buf, start, max, ( x ), ( sizeof( x ) - 1U ) ) == true )
591633

592634
if( skipLit_( "true" ) || skipLit_( "false" ) || skipLit_( "null" ) )
593635
{
@@ -1262,7 +1304,7 @@ static JSONStatus_t search( char * buf,
12621304
*/
12631305
JSONStatus_t JSON_Search( char * buf,
12641306
size_t max,
1265-
char * queryKey,
1307+
const char * queryKey,
12661308
size_t queryKeyLength,
12671309
char separator,
12681310
char ** outValue,

source/include/core_json.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ JSONStatus_t JSON_Validate( const char * buf,
158158
/* @[declare_json_search] */
159159
JSONStatus_t JSON_Search( char * buf,
160160
size_t max,
161-
char * queryKey,
161+
const char * queryKey,
162162
size_t queryKeyLength,
163163
char separator,
164164
char ** outValue,

test/cbmc/proofs/skipEscape/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CBMC_MAX_BUFSIZE=14
99

1010
UNWINDSET += skipEscape.0:$(CBMC_MAX_BUFSIZE)
1111
UNWINDSET += skipHexEscape.0:$(CBMC_MAX_BUFSIZE)
12+
UNWINDSET += skipOneHexEscape.0:$(CBMC_MAX_BUFSIZE)
1213

1314
include ../Makefile-json.common
1415

test/cbmc/proofs/skipEscape/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The proof runs in a few seconds and provides complete coverage of:
88
* hexToInt()
99
* skipEscape()
1010
* skipHexEscape()
11+
* skipOneHexEscape()
1112

1213
To run the proof.
1314
* Add cbmc, goto-cc, goto-instrument, goto-analyzer, and cbmc-viewer

tools/coverity/misra.config

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// MISRA C-2012 Rules
2+
3+
{
4+
version : "2.0",
5+
standard : "c2012",
6+
title: "Coverity MISRA Configuration",
7+
deviations : [
8+
{
9+
deviation: "Directive 4.9",
10+
category: "Advisory",
11+
reason: "Allow inclusion of function like macros."
12+
},
13+
{
14+
deviation: "Rule 15.4",
15+
category: "Advisory",
16+
reason: "Allow more then one break statement to terminate a loop"
17+
},
18+
{
19+
deviation: "Rule 19.2",
20+
category: "Advisory",
21+
reason: "Allow a union of a signed and unsigned type of identical sizes."
22+
},
23+
{
24+
deviation: "Rule 3.1",
25+
category: "Required",
26+
reason: "Allow nested comments. Documentation blocks contain comments for example code."
27+
},
28+
{
29+
deviation: "Rule 20.12",
30+
category: "Required",
31+
reason: "Allow use of assert(), which uses a parameter in both expanded and raw forms."
32+
},
33+
]
34+
}

0 commit comments

Comments
 (0)