├── README.md └── test_cases ├── argv-envp-access.c ├── incorrect-use-of-sizeof.c ├── incorrect-use-of-strncat.c ├── incorrect-use-of-strncpy-stpncpy-strlcpy.c ├── insecure-api-gets.c ├── insecure-api-scanf-etc.c ├── insecure-api-strcpy-stpcpy-strcat.c └── unterminated-string-strncpy-stpncpy.c /README.md: -------------------------------------------------------------------------------- 1 | # weggli-patterns 2 | Collections of patterns for weggli to find nice bugs 3 | 4 | 5 | ## find strcpy-like/memcpy calls with static arrays 6 | 7 | ``` 8 | weggli -R 'func=^str.*cpy$' '{char $b[_]; $func($b, _);}' source 9 | 10 | static char buf[256]; 11 | 12 | if ( var1 && obj->a ) 13 | { 14 | d = obj->a(obj->h); 15 | if ( e < 300 ) 16 | .. 17 | strcpy(someotherbuf,pValue); 18 | } 19 | else if(!strcmp("somestring",pParams[i].Name)) 20 | { 21 | if(pValue != NULL) 22 | strcpy(buf,pValue); 23 | ``` 24 | 25 | 26 | 27 | ## find strcpy/memcpy calls with length of source input instead of length of destination buffer 28 | 29 | ``` 30 | weggli --unique -R 'func=.*cpy$' '{$func($_, _($a), _($a));}' src 31 | test.c:371 32 | void some_function(char* conn) 33 | { 34 | .. 35 | 36 | strncpy(ps->var[0].value, conn, strlen(conn)); 37 | .. 38 | return; 39 | } 40 | ``` 41 | 42 | ``` 43 | weggli -R 'func=.*cpy$' '{$func($_, $a._, $a._);}' src 44 | test.c:897 45 | static int stuff( 46 | .. 47 | memcpy(buf, header->value.buf, header->value.length); 48 | .. 49 | } 50 | ``` 51 | 52 | ## strncpy-like with potential arithmetic errors 53 | 54 | ``` 55 | weggli --unique -R 'func=.*ncpy$' '{$func($_, _($a), $n - $m);}' source 56 | 57 | size_t m = strlen(test->user_data); 58 | size_t n = m + (s - test->c) - 5; 59 | strncpy(test->a, test->b, n - m); // n and m are unsigned, if m > n, buffer overflow 60 | 61 | ``` 62 | 63 | 64 | 65 | ## malloc-like calls with potential integer overflows 66 | 67 | ``` 68 | weggli -R '$fn=lloc' '{$size; $size=_+_; $fn($size);}' source 69 | weggli -R '$fn=lloc' '{$user_num=atoi(_);$fn($user_num);}' source 70 | ``` 71 | 72 | ## unitialized pointers 73 | 74 | ``` 75 | weggli '{ _* $p;NOT: $p = _;$func(&$p);}' source 76 | 77 | char *name; 78 | int id = a(val[i].parameterName, &name); 79 | ``` 80 | 81 | ## format string functions calls' return values to index buffers 82 | 83 | ``` 84 | weggli -R '$fn=printf$' '{$ret = $fn$($b,_,_);$b[$ret] = _;}' source 85 | ``` 86 | 87 | ## no space for zero terminator 88 | 89 | `weggli '{$len=strlen($buf);$dest=malloc($len);strcpy($dest,$buf);}' source` 90 | 91 | ``` 92 | weggli '{$dest=malloc(strlen($buf));strcpy($dest,$buf);}' test.c 14:29:41 93 | char *copy; 94 | copy = (char *)malloc(strlen(input)); 95 | strcpy(copy, input); 96 | return copy; 97 | ``` 98 | 99 | 100 | ## format string bugs 101 | 102 | `weggli -R '$fn=printf$' -R '$arg=[^"]*' '{$fn($arg);}' test2.c` 103 | 104 | This query doesn't work well for format string functions with length specifiers such as snprintf. Here is another one (also not perfect): 105 | 106 | ``` 107 | weggli -R '$fn=^[^n]*printf$' -R '$arg=[^"]*' '{$fn($arg);}' src #for fprintf, printf, etc 108 | 109 | weggli -R '$fn=nprintf$' -R '$arg=[^"]*' '{$fn($_,$_,$arg);}' src # for snprintf, etc 110 | ``` 111 | 112 | 113 | ## integer overflows 114 | 115 | ``` 116 | weggli '{$user_num=atoi(_);$user_num+_;}' source 117 | 118 | 119 | i = atoi(instanceNumber); 120 | if(i <= 0 || i > objN) return -1; 121 | return i + b; 122 | ``` 123 | 124 | ## typical buffer overruns in loops 125 | 126 | ### Find CVE 2017-9765 127 | 128 | ``` 129 | weggli ' { 130 | _ $buf[_]; $t = $buf;while (_) { $t; } 131 | }' toto.c 132 | 133 | 134 | toto.c:1395 135 | static soap_wchar 136 | soap_get_pi(struct soap *soap) 137 | { char buf[64]; 138 | register char *s = buf; 139 | register int i = sizeof(buf); 140 | register soap_wchar c = soap_getchar(soap); 141 | /* This is a quick way to parse XML PI and we could use a callback instead to 142 | * enable applications to intercept processing instructions */ 143 | while ((int)c != EOF && c != '?') 144 | { if (--i > 0) 145 | { if (soap_blank(c)) 146 | c = ' '; 147 | *s++ = (char)c; 148 | } 149 | c = soap_getchar(soap); 150 | } 151 | *s = '\0'; 152 | DBGLOG(TEST, SOAP_MESSAGE(fdebug, "XML PI \n", buf)); 153 | .. 154 | } 155 | ``` 156 | 157 | ## TOCTOU 158 | 159 | Needs more function names but you get the idea 160 | 161 | ``` 162 | weggli -R '$f1=(fopen|chmod|access|stat)' -R '$f2=(fopen|chmod|access|stat)' '{$f1($name);$f2($name);}' test3.c 15:02:42 163 | int main(void) { 164 | char *file_name; 165 | FILE *f_ptr; 166 | 167 | /* Initialize file_name */ 168 | 169 | f_ptr = fopen(file_name, "w"); 170 | if (f_ptr == NULL) { 171 | /* Handle error */ 172 | } 173 | 174 | /* ... */ 175 | 176 | if (chmod(file_name, S_IRUSR) == -1) { 177 | /* Handle error */ 178 | } 179 | } 180 | ``` 181 | 182 | 183 | 184 | ## double free 185 | 186 | ``` 187 | weggli -R '$fn=free' '{$fn($a);not: $a=_;not: return _;$fn($a);}' doublefree.c 188 | 189 | int bad_code1() { 190 | char *var = malloc(sizeof(char) * 10); 191 | free(var); 192 | free(var); // <-bug 193 | return 0; 194 | } 195 | ``` 196 | 197 | 198 | ## use after free 199 | 200 | ``` 201 | weggli -R '$fn=free' '{$fn($a);not: $a=_;not: return _;_($a);}' use-after-free.c 202 | 203 | 204 | use-after-free.c:8 205 | int bad_code1() { 206 | NAME *var; 207 | var = (NAME *)malloc(sizeof(struct name)); 208 | free(var); 209 | var->func("use after free"); 210 | return 0; 211 | } 212 | ``` 213 | 214 | ## find buffers passed as function arguments and freed within the function body 215 | 216 | ``` 217 | weggli '_ $fn(_ $buf) { 218 | free($buf); 219 | }' source 220 | test.c:930 221 | int parse_stuff(char* Ctx) 222 | { 223 | 224 | test(Ctx, 0, 0, 1); 225 | .. 226 | #endif 227 | } 228 | 229 | //Free allocated memory 230 | free(Ctx->bufferCtx.pBuf); 231 | free(Ctx); // <-- 232 | 233 | return -1; 234 | } 235 | ``` 236 | 237 | Each finding must be analyzed to check if the freed buffer is used by the caller or freed one more time by mistake. 238 | 239 | 240 | 241 | # 0xdea semgrep's rules 242 | 243 | ## buffer overflows 244 | 245 | [**insecure-api-gets**](https://github.com/0xdea/semgrep-rules/blob/main/c/insecure-api-gets.yaml). Use of the insecure API function gets(). 246 | 247 | ```c 248 | weggli '{gets(_);}' test_cases/insecure-api-gets.c 249 | /test_cases/insecure-api-gets.c:7 250 | void get_string() 251 | { 252 | char buf[BUFSIZE]; 253 | 254 | // ruleid: raptor-insecure-api-gets 255 | gets(buf); 256 | } 257 | ``` 258 | 259 | 260 | [**insecure-api-strcpy-stpcpy-strcat**](https://github.com/0xdea/semgrep-rules/blob/main/c/insecure-api-strcpy-stpcpy-strcat.yaml). Use of potentially insecure API functions strcpy(), stpcpy(), strcat(). 261 | 262 | ```c 263 | weggli -R '$fn=(strcpy|stpcpy|strcat|wcscpy|wcpcpy|wcscat)' '{$fn(_);}' test_cases/insecure-api-strcpy-stpcpy-strcat.c 264 | test_cases/insecure-api-strcpy-stpcpy-strcat.c:74 265 | int process_email(char *email) 266 | { 267 | .. 268 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 269 | strcpy(domain, delim); 270 | 271 | if (!strchr(delim, '.')) 272 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 273 | strcat(domain, default_domain); 274 | 275 | // ... 276 | } 277 | test_cases/insecure-api-strcpy-stpcpy-strcat.c:105 278 | void process_address(int sockfd) 279 | { 280 | .. 281 | 282 | if (ptr) 283 | *ptr++ = '\0'; 284 | 285 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 286 | strcpy(username, netbuf); 287 | 288 | if (ptr) 289 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 290 | strcpy(domain, ptr); 291 | 292 | .. 293 | } 294 | ``` 295 | 296 | 297 | [**insecure-api-sprintf-vsprintf**](https://github.com/0xdea/semgrep-rules/blob/main/c/insecure-api-sprintf-vsprintf.yaml). Use of potentially insecure API functions sprintf() and vsprintf(). 298 | 299 | This one is harder to make using weggli because of FMT regex. 300 | 301 | 302 | [**insecure-api-scanf-etc**](https://github.com/0xdea/semgrep-rules/blob/main/c/insecure-api-scanf-etc.yaml). Use of potentially insecure API functions in the scanf() family. 303 | 304 | Same 305 | 306 | [**incorrect-use-of-strncat**](https://github.com/0xdea/semgrep-rules/blob/main/c/incorrect-use-of-strncat.yaml). Wrong size argument passed to strncat(). 307 | 308 | Unfortunately, it's not possible to match buffer length with weggli: https://github.com/weggli-rs/weggli/issues/59 309 | 310 | 311 | So, this one won't work: `weggli -v '{_ $dst[$len];strncat($dst, _, $len);}' test_cases/incorrect-use-of-strncat.c` 312 | 313 | 314 | If you run this one `weggli -v '{_ $dst[_];strncat($dst, _, _);}' test_cases/incorrect-use-of-strncat.c` instead, you can match them with many false positive. 315 | 316 | For the other queries of the pattern [here](https://github.com/0xdea/semgrep-rules/blob/main/c/incorrect-use-of-strncat.yaml), this query works: 317 | 318 | ```c 319 | weggli -u '{_ $dst[_];strncat($dst, _, _(strlen(_)));}' -p '{_ $dst[_];strncat($dst, _, sizeof(_));}' test_cases/incorrect-use-of-strncat.c 320 | test_cases/incorrect-use-of-strncat.c:32 321 | int copy_data3(char *username) 322 | { 323 | char buf[1024]; 324 | 325 | strcpy(buf, "username is: "); 326 | // ruleid: raptor-incorrect-use-of-strncat 327 | strncat(buf, username, sizeof(buf) - strlen(buf)); 328 | 329 | log("%s\n", buf); 330 | 331 | return 0; 332 | } 333 | test_cases/incorrect-use-of-strncat.c:45 334 | int good(char *username) 335 | { 336 | char buf[1024]; 337 | 338 | strcpy(buf, "username is: "); 339 | // ok: raptor-incorrect-use-of-strncat 340 | strncat(buf, username, sizeof(buf) - strlen(buf) - 1); 341 | 342 | log("%s\n", buf); 343 | 344 | return 0; 345 | } 346 | test_cases/incorrect-use-of-strncat.c:6 347 | int copy_data(char *username) 348 | { 349 | char buf[1024]; 350 | 351 | strcpy(buf, "username is: "); 352 | // ruleid: raptor-incorrect-use-of-strncat 353 | strncat(buf, username, sizeof(buf)); 354 | 355 | log("%s\n", buf); 356 | 357 | return 0; 358 | } 359 | ``` 360 | 361 | 362 | 363 | * [**incorrect-use-of-strncpy-stpncpy-strlcpy**](https://github.com/0xdea/semgrep-rules/blob/main/c/incorrect-use-of-strncpy-stpncpy-strlcpy.yaml). Wrong size argument passed to strncpy(), stpncpy(), strlcpy(). 364 | 365 | Same remark as above. 366 | 367 | ```c 368 | weggli -R '$fn=(strncpy|stpncpy|strlcpy)' '{$fn($dst, $src, _($src));}' test_cases/incorrect-use-of-strncpy-stpncpy-strlcpy.c 15:03:23 369 | test_cases/incorrect-use-of-strncpy-stpncpy-strlcpy.c:3 370 | void test_func() 371 | { 372 | char source[21] = "the character string"; 373 | char dest[12]; 374 | 375 | // ruleid: raptor-incorrect-use-of-strncpy-stpncpy-strlcpy 376 | strncpy(dest, source, sizeof(source)-1); 377 | } 378 | test_cases/incorrect-use-of-strncpy-stpncpy-strlcpy.c:120 379 | int 380 | main(int argc, char *argv[]) 381 | .. 382 | up->p_state = (info.pr_nlwp == 0? ZOMBIE : RUNNING); 383 | up->p_time = 0; 384 | up->p_ctime = 0; 385 | up->p_igintr = 0; 386 | // ruleid: raptor-incorrect-use-of-strncpy-stpncpy-strlcpy 387 | (void) strncpy(up->p_comm, info.pr_fname, 388 | sizeof (info.pr_fname)); 389 | up->p_args[0] = 0; 390 | 391 | if (up->p_state != NONE && up->p_state != ZOMBIE) { 392 | (void) strcpy(fname, "status"); 393 | 394 | .. 395 | } 396 | ``` 397 | 398 | * [**incorrect-use-of-sizeof**](https://github.com/0xdea/semgrep-rules/blob/main/c/incorrect-use-of-sizeof.yaml). Accidental use of the sizeof() operator on a pointer instead of its target. 399 | 400 | ```c 401 | weggli -R '$fn=alloc$' '{$ptr = $fn(_); sizeof($ptr);}' -p '{_ *$p;sizeof($p);}' test_cases/incorrect-use-of-sizeof.c 15:20:49 402 | test_cases/incorrect-use-of-sizeof.c:8 403 | void bad1() 404 | { 405 | double *foo; 406 | 407 | // ruleid: raptor-incorrect-use-of-sizeof 408 | foo = (double *)malloc(sizeof(foo)); 409 | } 410 | test_cases/incorrect-use-of-sizeof.c:41 411 | void bad3() 412 | { 413 | AnObj *o = (AnObj *) malloc(sizeof(AnObj)); 414 | // ruleid: raptor-incorrect-use-of-sizeof 415 | memset(o, 0x0, sizeof(o)); 416 | } 417 | test_cases/incorrect-use-of-sizeof.c:48 418 | char *read_username(int sockfd) 419 | { 420 | char *buffer, *style, userstring[1024]; 421 | int i; 422 | 423 | buffer = (char *)malloc(1024); 424 | 425 | if (!buffer) { 426 | error("buffer allocation failed: %m"); 427 | return NULL; 428 | } 429 | .. 430 | *style++ = '\0'; 431 | sprintf(buffer, "username=%.32s", userstring); 432 | 433 | if (style) 434 | // ruleid: raptor-incorrect-use-of-sizeof 435 | snprintf(buffer, sizeof(buffer) - strlen(buffer) - 1, ", style=%s\n", style); 436 | 437 | return buffer; 438 | } 439 | test_cases/incorrect-use-of-sizeof.c:8 440 | void bad1() 441 | { 442 | double *foo; 443 | 444 | // ruleid: raptor-incorrect-use-of-sizeof 445 | foo = (double *)malloc(sizeof(foo)); 446 | } 447 | test_cases/incorrect-use-of-sizeof.c:48 448 | char *read_username(int sockfd) 449 | { 450 | char *buffer, *style, userstring[1024]; 451 | int i; 452 | 453 | buffer = (char *)malloc(1024); 454 | 455 | if (!buffer) { 456 | .. 457 | *style++ = '\0'; 458 | sprintf(buffer, "username=%.32s", userstring); 459 | 460 | if (style) 461 | // ruleid: raptor-incorrect-use-of-sizeof 462 | snprintf(buffer, sizeof(buffer) - strlen(buffer) - 1, ", style=%s\n", style); 463 | 464 | return buffer; 465 | } 466 | ``` 467 | 468 | [**unterminated-string-strncpy-stpncpy**](https://github.com/0xdea/semgrep-rules/blob/main/c/unterminated-string-strncpy-stpncpy.yaml). Lack of explicit null-termination after strncpy() and stpncpy(). 469 | 470 | ```c 471 | weggli -R '$fn=(strncpy|stpncpy|strlcpy|strncpy|wcpncpy|wcsncpy)' '{$fn($dst, $src, _);not: $dst[_] = _;}' test_cases/unterminated-string-strncpy-stpncpy.c 472 | test_cases/unterminated-string-strncpy-stpncpy.c:8 473 | void copy_string1(char *string) 474 | { 475 | char buf[BUFSIZE]; 476 | 477 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 478 | strncpy(buf, string, BUFSIZE); 479 | } 480 | test_cases/unterminated-string-strncpy-stpncpy.c:16 481 | void copy_string2(char *string) 482 | { 483 | char buf[BUFSIZE]; 484 | 485 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 486 | stpncpy(buf, string, BUFSIZE); 487 | } 488 | test_cases/unterminated-string-strncpy-stpncpy.c:24 489 | int test_func() 490 | { 491 | char longString[] = "String signifying nothing"; 492 | char shortString[16]; 493 | 494 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 495 | strncpy(shortString, longString, 16); 496 | printf("The last character in shortString is: %c (%1$x)\n", shortString[15]); 497 | return 0; 498 | } 499 | test_cases/unterminated-string-strncpy-stpncpy.c:51 500 | void authenticate(int sockfd) 501 | { 502 | .. 503 | read_string(buffer, size); 504 | 505 | switch(cmd) { 506 | case USERNAME: 507 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 508 | strncpy(user, buffer, sizeof(user)); 509 | if (!is_username_valid(user)) 510 | goto fail; 511 | break; 512 | // ... 513 | } 514 | .. 515 | } 516 | test_cases/unterminated-string-strncpy-stpncpy.c:79 517 | int process_email(char *email) 518 | { 519 | char buf[1024], *domain; 520 | 521 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 522 | strncpy(buf, email, sizeof(buf)); 523 | 524 | domain = strchr(buf, '@'); 525 | if(!domain) 526 | return -1; 527 | 528 | .. 529 | } 530 | ``` 531 | 532 | 533 | * [**off-by-one**](https://github.com/0xdea/semgrep-rules/blob/main/c/off-by-one.yaml). Potential off-by-one error. 534 | * [**pointer-subtraction**](https://github.com/0xdea/semgrep-rules/blob/main/c/pointer-subtraction.yaml). Potential use of pointer subtraction to determine size. 535 | * [**unsafe-ret-snprintf-vsnprintf**](https://github.com/0xdea/semgrep-rules/blob/main/c/unsafe-ret-snprintf-vsnprintf.yaml). Potentially unsafe use of the return value of snprintf() and vsnprintf(). 536 | * [**unsafe-ret-strlcpy-strlcat**](https://github.com/0xdea/semgrep-rules/blob/main/c/unsafe-ret-strlcpy-strlcat.yaml). Potentially unsafe use of the return value of strlcpy() and strlcat(). 537 | * [**write-into-stack-buffer**](https://github.com/0xdea/semgrep-rules/blob/main/c/write-into-stack-buffer.yaml). Direct writes into buffers allocated on the stack. 538 | 539 | ## miscellaneous 540 | 541 | 542 | [**argv-envp-access**](https://github.com/0xdea/semgrep-rules/blob/main/c/argv-envp-access.yaml). Command-line argument or environment variable access. 543 | 544 | `weggli -R '$arg=(argv|envp)' '{$arg;}' test_cases/argv-envp-access.c` 545 | 546 | ```c 547 | test_cases/argv-envp-access.c:6 548 | int main(int argc, char** argv) 549 | { 550 | char cmd[CMD_MAX] = "/usr/bin/cat "; 551 | // ruleid: raptor-argv-envp-access 552 | strcat(cmd, argv[1]); 553 | system(cmd); 554 | 555 | return 0; 556 | } 557 | ``` 558 | 559 | 560 | 561 | # Original examples 562 | 563 | Examples by felixwilhelm 564 | 565 | Calls to memcpy that write into a stack-buffer: 566 | 567 | ```c 568 | weggli '{ 569 | _ $buf[_]; 570 | memcpy($buf,_,_); 571 | }' ./target/src 572 | ``` 573 | 574 | Calls to foo that don't check the return value: 575 | ```c 576 | weggli '{ 577 | strict: foo(_); 578 | }' ./target/src 579 | ``` 580 | 581 | Potentially vulnerable snprintf() users: 582 | ```c 583 | weggli '{ 584 | $ret = snprintf($b,_,_); 585 | $b[$ret] = _; 586 | }' ./target/src 587 | ``` 588 | 589 | Potentially uninitialized pointers: 590 | ```c 591 | weggli '{ _* $p; 592 | NOT: $p = _; 593 | $func(&$p); 594 | }' ./target/src 595 | ``` 596 | 597 | Potentially insecure WeakPtr usage: 598 | ```cpp 599 | weggli --cpp '{ 600 | $x = _.GetWeakPtr(); 601 | DCHECK($x); 602 | $x->_;}' ./target/src 603 | ``` 604 | 605 | Debug only iterator validation: 606 | ```cpp 607 | weggli -X 'DCHECK(_!=_.end());' ./target/src 608 | ``` 609 | 610 | Functions that perform writes into a stack-buffer based on 611 | a function argument. 612 | ```c 613 | weggli '_ $fn(_ $limit) { 614 | _ $buf[_]; 615 | for (_; $i<$limit; _) { 616 | $buf[$i]=_; 617 | } 618 | }' ./target/src 619 | ``` 620 | 621 | Functions with the string decode in their name 622 | ```c 623 | weggli -R func=decode '_ $func(_) {_;}' 624 | ``` 625 | 626 | Encoding/Conversion functions 627 | ```c 628 | weggli '_ $func($t *$input, $t2 *$output) { 629 | for (_($i);_;_) { 630 | $input[$i]=_($output); 631 | } 632 | }' ./target/src 633 | ``` 634 | 635 | -------------------------------------------------------------------------------- /test_cases/argv-envp-access.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char** argv) 7 | { 8 | char cmd[CMD_MAX] = "/usr/bin/cat "; 9 | // ruleid: raptor-argv-envp-access 10 | strcat(cmd, argv[1]); 11 | system(cmd); 12 | 13 | return 0; 14 | } -------------------------------------------------------------------------------- /test_cases/incorrect-use-of-sizeof.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void bad1() 9 | { 10 | double *foo; 11 | 12 | // ruleid: raptor-incorrect-use-of-sizeof 13 | foo = (double *)malloc(sizeof(foo)); 14 | } 15 | 16 | void good1() 17 | { 18 | double *foo; 19 | 20 | // ok: raptor-incorrect-use-of-sizeof 21 | foo = (double *)malloc(sizeof(*foo)); 22 | } 23 | 24 | void bad2(char *buf) 25 | { 26 | size_t size; 27 | 28 | // ruleid: raptor-incorrect-use-of-sizeof 29 | size = sizeof(buf); 30 | } 31 | 32 | void good2() 33 | { 34 | char buf[256]; 35 | size_t size; 36 | 37 | // ok: raptor-incorrect-use-of-sizeof 38 | size = sizeof(buf); 39 | } 40 | 41 | void bad3() 42 | { 43 | AnObj *o = (AnObj *) malloc(sizeof(AnObj)); 44 | // ruleid: raptor-incorrect-use-of-sizeof 45 | memset(o, 0x0, sizeof(o)); 46 | } 47 | 48 | char *read_username(int sockfd) 49 | { 50 | char *buffer, *style, userstring[1024]; 51 | int i; 52 | 53 | buffer = (char *)malloc(1024); 54 | 55 | if (!buffer) { 56 | error("buffer allocation failed: %m"); 57 | return NULL; 58 | } 59 | 60 | if (read(sockfd, userstring, sizeof(userstring) - 1) <= 0) { 61 | free(buffer); 62 | error("read failure: %m"); 63 | return NULL; 64 | } 65 | userstring[sizeof(userstring) - 1] = '\0'; 66 | style = strchr(userstring, ':'); 67 | 68 | if (style) 69 | *style++ = '\0'; 70 | sprintf(buffer, "username=%.32s", userstring); 71 | 72 | if (style) 73 | // ruleid: raptor-incorrect-use-of-sizeof 74 | snprintf(buffer, sizeof(buffer) - strlen(buffer) - 1, ", style=%s\n", style); 75 | 76 | return buffer; 77 | } 78 | 79 | char *username = "admin"; 80 | char *pass = "password"; 81 | 82 | int AuthenticateUser(char *inUser, char *inPass) 83 | { 84 | // ruleid: raptor-incorrect-use-of-sizeof 85 | printf("Sizeof username = %d\n", sizeof(username)); 86 | // ruleid: raptor-incorrect-use-of-sizeof 87 | printf("Sizeof pass = %d\n", sizeof(pass)); 88 | 89 | // ruleid: raptor-incorrect-use-of-sizeof 90 | if (strncmp(username, inUser, sizeof(username))) { 91 | printf("Auth failure of username using sizeof\n"); 92 | return(AUTH_FAIL); 93 | } 94 | 95 | // ruleid: raptor-incorrect-use-of-sizeof 96 | if (!strncmp(pass, inPass, sizeof(pass))) { 97 | printf("Auth success of password using sizeof\n"); 98 | return(AUTH_SUCCESS); 99 | } else { 100 | printf("Auth fail of password using sizeof\n"); 101 | return(AUTH_FAIL); 102 | } 103 | } 104 | 105 | int main (int argc, char **argv) 106 | { 107 | int authResult; 108 | 109 | if (argc < 3) { 110 | ExitError("Usage: Provide a username and password"); 111 | } 112 | authResult = AuthenticateUser(argv[1], argv[2]); 113 | if (authResult != AUTH_SUCCESS) { 114 | ExitError("Authentication failed"); 115 | } else { 116 | DoAuthenticatedTask(argv[1]); 117 | } 118 | return 0; 119 | } -------------------------------------------------------------------------------- /test_cases/incorrect-use-of-strncat.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | #include 5 | 6 | int copy_data(char *username) 7 | { 8 | char buf[1024]; 9 | 10 | strcpy(buf, "username is: "); 11 | // ruleid: raptor-incorrect-use-of-strncat 12 | strncat(buf, username, sizeof(buf)); 13 | 14 | log("%s\n", buf); 15 | 16 | return 0; 17 | } 18 | 19 | int copy_data2(char *username) 20 | { 21 | char buf[1024]; 22 | 23 | strcpy(buf, "username is: "); 24 | // ruleid: raptor-incorrect-use-of-strncat 25 | strncat(buf, username, 1024); 26 | 27 | log("%s\n", buf); 28 | 29 | return 0; 30 | } 31 | 32 | int copy_data3(char *username) 33 | { 34 | char buf[1024]; 35 | 36 | strcpy(buf, "username is: "); 37 | // ruleid: raptor-incorrect-use-of-strncat 38 | strncat(buf, username, sizeof(buf) - strlen(buf)); 39 | 40 | log("%s\n", buf); 41 | 42 | return 0; 43 | } 44 | 45 | int good(char *username) 46 | { 47 | char buf[1024]; 48 | 49 | strcpy(buf, "username is: "); 50 | // ok: raptor-incorrect-use-of-strncat 51 | strncat(buf, username, sizeof(buf) - strlen(buf) - 1); 52 | 53 | log("%s\n", buf); 54 | 55 | return 0; 56 | } 57 | 58 | int main() 59 | { 60 | printf("Hello, World!"); 61 | return 0; 62 | } -------------------------------------------------------------------------------- /test_cases/incorrect-use-of-strncpy-stpncpy-strlcpy.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | void test_func() 4 | { 5 | char source[21] = "the character string"; 6 | char dest[12]; 7 | 8 | // ruleid: raptor-incorrect-use-of-strncpy-stpncpy-strlcpy 9 | strncpy(dest, source, sizeof(source)-1); 10 | } 11 | 12 | // https://raw.githubusercontent.com/illumos/illumos-gate/61aaa916808c601f9ee36d96c05ee9dac211d09e/usr/src/cmd/whodo/whodo.c 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include /* /proc header file */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | /* 34 | * Use the full lengths from utmpx for user and line. 35 | */ 36 | #define NMAX (sizeof (((struct utmpx *)0)->ut_user)) 37 | #define LMAX (sizeof (((struct utmpx *)0)->ut_line)) 38 | 39 | /* Print minimum field widths. */ 40 | #define LOGIN_WIDTH 8 41 | #define LINE_WIDTH 8 42 | 43 | #define DIV60(t) ((t+30)/60) /* x/60 rounded */ 44 | 45 | #ifdef ERR 46 | #undef ERR 47 | #endif 48 | #define ERR (-1) 49 | 50 | #define DEVNAMELEN 14 51 | #define HSIZE 256 /* size of process hash table */ 52 | #define PROCDIR "/proc" 53 | #define INITPROCESS (pid_t)1 /* init process pid */ 54 | #define NONE 'n' /* no state */ 55 | #define RUNNING 'r' /* runnable process */ 56 | #define ZOMBIE 'z' /* zombie process */ 57 | #define VISITED 'v' /* marked node as visited */ 58 | 59 | static int ndevs; /* number of configured devices */ 60 | static int maxdev; /* slots for configured devices */ 61 | #define DNINCR 100 62 | static struct devl { /* device list */ 63 | char dname[DEVNAMELEN]; /* device name */ 64 | dev_t ddev; /* device number */ 65 | } *devl; 66 | 67 | struct uproc { 68 | pid_t p_upid; /* user process id */ 69 | char p_state; /* numeric value of process state */ 70 | dev_t p_ttyd; /* controlling tty of process */ 71 | time_t p_time; /* ticks of user & system time */ 72 | time_t p_ctime; /* ticks of child user & system time */ 73 | int p_igintr; /* 1=ignores SIGQUIT and SIGINT */ 74 | char p_comm[PRARGSZ+1]; /* command */ 75 | char p_args[PRARGSZ+1]; /* command line arguments */ 76 | struct uproc *p_child, /* first child pointer */ 77 | *p_sibling, /* sibling pointer */ 78 | *p_pgrplink, /* pgrp link */ 79 | *p_link; /* hash table chain pointer */ 80 | }; 81 | 82 | /* 83 | * define hash table for struct uproc 84 | * Hash function uses process id 85 | * and the size of the hash table(HSIZE) 86 | * to determine process index into the table. 87 | */ 88 | static struct uproc pr_htbl[HSIZE]; 89 | 90 | static struct uproc *findhash(pid_t); 91 | static time_t findidle(char *); 92 | static void clnarglist(char *); 93 | static void showproc(struct uproc *); 94 | static void showtotals(struct uproc *); 95 | static void calctotals(struct uproc *); 96 | static char *getty(dev_t); 97 | static void prttime(time_t, int); 98 | static void prtat(time_t *); 99 | 100 | static char *prog; 101 | static int header = 1; /* true if -h flag: don't print heading */ 102 | static int lflag = 0; /* true if -l flag: w command format */ 103 | static char *sel_user; /* login of particular user selected */ 104 | static time_t now; /* current time of day */ 105 | static time_t uptime; /* time of last reboot & elapsed time since */ 106 | static int nusers; /* number of users logged in now */ 107 | static time_t idle; /* number of minutes user is idle */ 108 | static time_t jobtime; /* total cpu time visible */ 109 | static char doing[520]; /* process attached to terminal */ 110 | static time_t proctime; /* cpu time of process in doing */ 111 | static int empty; 112 | static pid_t curpid; 113 | 114 | #if SIGQUIT > SIGINT 115 | #define ACTSIZE SIGQUIT 116 | #else 117 | #define ACTSIZE SIGINT 118 | #endif 119 | 120 | int 121 | main(int argc, char *argv[]) 122 | { 123 | struct utmpx *ut; 124 | struct utmpx *utmpbegin; 125 | struct utmpx *utmpend; 126 | struct utmpx *utp; 127 | struct tm *tm; 128 | struct uproc *up, *parent, *pgrp; 129 | struct psinfo info; 130 | struct sigaction actinfo[ACTSIZE]; 131 | struct pstatus statinfo; 132 | size_t size; 133 | struct stat sbuf; 134 | struct utsname uts; 135 | DIR *dirp; 136 | struct dirent *dp; 137 | char pname[64]; 138 | char *fname; 139 | int procfd; 140 | int i; 141 | int days, hrs, mins; 142 | int entries; 143 | 144 | /* 145 | * This program needs the proc_owner privilege 146 | */ 147 | (void) __init_suid_priv(PU_CLEARLIMITSET, PRIV_PROC_OWNER, 148 | (char *)NULL); 149 | 150 | (void) setlocale(LC_ALL, ""); 151 | #if !defined(TEXT_DOMAIN) 152 | #define TEXT_DOMAIN "SYS_TEST" 153 | #endif 154 | (void) textdomain(TEXT_DOMAIN); 155 | 156 | prog = argv[0]; 157 | 158 | while (argc > 1) { 159 | if (argv[1][0] == '-') { 160 | for (i = 1; argv[1][i]; i++) { 161 | switch (argv[1][i]) { 162 | 163 | case 'h': 164 | header = 0; 165 | break; 166 | 167 | case 'l': 168 | lflag++; 169 | break; 170 | 171 | default: 172 | (void) printf(gettext( 173 | "usage: %s [ -hl ] [ user ]\n"), 174 | prog); 175 | exit(1); 176 | } 177 | } 178 | } else { 179 | if (!isalnum(argv[1][0]) || argc > 2) { 180 | (void) printf(gettext( 181 | "usage: %s [ -hl ] [ user ]\n"), prog); 182 | exit(1); 183 | } else 184 | sel_user = argv[1]; 185 | } 186 | argc--; argv++; 187 | } 188 | 189 | /* 190 | * read the UTMPX_FILE (contains information about 191 | * each logged in user) 192 | */ 193 | if (stat(UTMPX_FILE, &sbuf) == ERR) { 194 | (void) fprintf(stderr, gettext("%s: stat error of %s: %s\n"), 195 | prog, UTMPX_FILE, strerror(errno)); 196 | exit(1); 197 | } 198 | entries = sbuf.st_size / sizeof (struct futmpx); 199 | size = sizeof (struct utmpx) * entries; 200 | 201 | if ((ut = malloc(size)) == NULL) { 202 | (void) fprintf(stderr, gettext("%s: malloc error of %s: %s\n"), 203 | prog, UTMPX_FILE, strerror(errno)); 204 | exit(1); 205 | } 206 | 207 | (void) utmpxname(UTMPX_FILE); 208 | 209 | utmpbegin = ut; 210 | /* LINTED pointer cast may result in improper alignment */ 211 | utmpend = (struct utmpx *)((char *)utmpbegin + size); 212 | 213 | setutxent(); 214 | while ((ut < utmpend) && ((utp = getutxent()) != NULL)) 215 | (void) memcpy(ut++, utp, sizeof (*ut)); 216 | endutxent(); 217 | 218 | (void) time(&now); /* get current time */ 219 | 220 | if (header) { /* print a header */ 221 | if (lflag) { /* w command format header */ 222 | prtat(&now); 223 | for (ut = utmpbegin; ut < utmpend; ut++) { 224 | if (ut->ut_type == USER_PROCESS) { 225 | nusers++; 226 | } else if (ut->ut_type == BOOT_TIME) { 227 | uptime = now - ut->ut_xtime; 228 | uptime += 30; 229 | days = uptime / (60*60*24); 230 | uptime %= (60*60*24); 231 | hrs = uptime / (60*60); 232 | uptime %= (60*60); 233 | mins = uptime / 60; 234 | 235 | (void) printf(dcgettext(NULL, 236 | "up %d day(s), %d hr(s), " 237 | "%d min(s)", LC_TIME), 238 | days, hrs, mins); 239 | } 240 | } 241 | 242 | ut = utmpbegin; /* rewind utmp data */ 243 | (void) printf(dcgettext(NULL, 244 | " %d user(s)\n", LC_TIME), nusers); 245 | (void) printf(dcgettext(NULL, "User tty " 246 | "login@ idle JCPU PCPU what\n", 247 | LC_TIME)); 248 | } else { /* standard whodo header */ 249 | char date_buf[100]; 250 | 251 | /* 252 | * print current time and date 253 | */ 254 | (void) strftime(date_buf, sizeof (date_buf), 255 | "%c", localtime(&now)); 256 | (void) printf("%s\n", date_buf); 257 | 258 | /* 259 | * print system name 260 | */ 261 | (void) uname(&uts); 262 | (void) printf("%s\n", uts.nodename); 263 | } 264 | } 265 | 266 | /* 267 | * loop through /proc, reading info about each process 268 | * and build the parent/child tree 269 | */ 270 | if (!(dirp = opendir(PROCDIR))) { 271 | (void) fprintf(stderr, gettext("%s: could not open %s: %s\n"), 272 | prog, PROCDIR, strerror(errno)); 273 | exit(1); 274 | } 275 | 276 | while ((dp = readdir(dirp)) != NULL) { 277 | if (dp->d_name[0] == '.') 278 | continue; 279 | retry: 280 | (void) snprintf(pname, sizeof (pname), 281 | "%s/%s/", PROCDIR, dp->d_name); 282 | fname = pname + strlen(pname); 283 | (void) strcpy(fname, "psinfo"); 284 | if ((procfd = open(pname, O_RDONLY)) < 0) 285 | continue; 286 | if (read(procfd, &info, sizeof (info)) != sizeof (info)) { 287 | int err = errno; 288 | (void) close(procfd); 289 | if (err == EAGAIN) 290 | goto retry; 291 | if (err != ENOENT) 292 | (void) fprintf(stderr, gettext( 293 | "%s: read() failed on %s: %s\n"), 294 | prog, pname, strerror(err)); 295 | continue; 296 | } 297 | (void) close(procfd); 298 | 299 | up = findhash(info.pr_pid); 300 | up->p_ttyd = info.pr_ttydev; 301 | up->p_state = (info.pr_nlwp == 0? ZOMBIE : RUNNING); 302 | up->p_time = 0; 303 | up->p_ctime = 0; 304 | up->p_igintr = 0; 305 | // ruleid: raptor-incorrect-use-of-strncpy-stpncpy-strlcpy 306 | (void) strncpy(up->p_comm, info.pr_fname, 307 | sizeof (info.pr_fname)); 308 | up->p_args[0] = 0; 309 | 310 | if (up->p_state != NONE && up->p_state != ZOMBIE) { 311 | (void) strcpy(fname, "status"); 312 | 313 | /* now we need the proc_owner privilege */ 314 | (void) __priv_bracket(PRIV_ON); 315 | 316 | procfd = open(pname, O_RDONLY); 317 | 318 | /* drop proc_owner privilege after open */ 319 | (void) __priv_bracket(PRIV_OFF); 320 | 321 | if (procfd < 0) 322 | continue; 323 | 324 | if (read(procfd, &statinfo, sizeof (statinfo)) 325 | != sizeof (statinfo)) { 326 | int err = errno; 327 | (void) close(procfd); 328 | if (err == EAGAIN) 329 | goto retry; 330 | if (err != ENOENT) 331 | (void) fprintf(stderr, gettext( 332 | "%s: read() failed on %s: %s \n"), 333 | prog, pname, strerror(err)); 334 | continue; 335 | } 336 | (void) close(procfd); 337 | 338 | up->p_time = statinfo.pr_utime.tv_sec + 339 | statinfo.pr_stime.tv_sec; 340 | up->p_ctime = statinfo.pr_cutime.tv_sec + 341 | statinfo.pr_cstime.tv_sec; 342 | 343 | (void) strcpy(fname, "sigact"); 344 | 345 | /* now we need the proc_owner privilege */ 346 | (void) __priv_bracket(PRIV_ON); 347 | 348 | procfd = open(pname, O_RDONLY); 349 | 350 | /* drop proc_owner privilege after open */ 351 | (void) __priv_bracket(PRIV_OFF); 352 | 353 | if (procfd < 0) 354 | continue; 355 | if (read(procfd, actinfo, sizeof (actinfo)) 356 | != sizeof (actinfo)) { 357 | int err = errno; 358 | (void) close(procfd); 359 | if (err == EAGAIN) 360 | goto retry; 361 | if (err != ENOENT) 362 | (void) fprintf(stderr, gettext( 363 | "%s: read() failed on %s: %s \n"), 364 | prog, pname, strerror(err)); 365 | continue; 366 | } 367 | (void) close(procfd); 368 | 369 | up->p_igintr = 370 | actinfo[SIGINT-1].sa_handler == SIG_IGN && 371 | actinfo[SIGQUIT-1].sa_handler == SIG_IGN; 372 | 373 | up->p_args[0] = 0; 374 | 375 | /* 376 | * Process args if there's a chance we'll print it. 377 | */ 378 | if (lflag) { /* w command needs args */ 379 | clnarglist(info.pr_psargs); 380 | (void) strcpy(up->p_args, info.pr_psargs); 381 | if (up->p_args[0] == 0 || 382 | up->p_args[0] == '-' && 383 | up->p_args[1] <= ' ' || 384 | up->p_args[0] == '?') { 385 | (void) strcat(up->p_args, " ("); 386 | (void) strcat(up->p_args, up->p_comm); 387 | (void) strcat(up->p_args, ")"); 388 | } 389 | } 390 | 391 | } 392 | 393 | /* 394 | * link pgrp together in case parents go away 395 | * Pgrp chain is a single linked list originating 396 | * from the pgrp leader to its group member. 397 | */ 398 | if (info.pr_pgid != info.pr_pid) { /* not pgrp leader */ 399 | pgrp = findhash(info.pr_pgid); 400 | up->p_pgrplink = pgrp->p_pgrplink; 401 | pgrp->p_pgrplink = up; 402 | } 403 | parent = findhash(info.pr_ppid); 404 | 405 | /* if this is the new member, link it in */ 406 | if (parent->p_upid != INITPROCESS) { 407 | if (parent->p_child) { 408 | up->p_sibling = parent->p_child; 409 | up->p_child = 0; 410 | } 411 | parent->p_child = up; 412 | } 413 | 414 | } 415 | 416 | /* revert to non-privileged user */ 417 | (void) __priv_relinquish(); 418 | 419 | (void) closedir(dirp); 420 | (void) time(&now); /* get current time */ 421 | 422 | /* 423 | * loop through utmpx file, printing process info 424 | * about each logged in user 425 | */ 426 | for (ut = utmpbegin; ut < utmpend; ut++) { 427 | time_t tim; 428 | 429 | if (ut->ut_type != USER_PROCESS) 430 | continue; 431 | if (sel_user && strncmp(ut->ut_name, sel_user, NMAX) != 0) 432 | continue; /* we're looking for somebody else */ 433 | if (lflag) { /* -l flag format (w command) */ 434 | /* print login name of the user */ 435 | (void) printf("%-*.*s ", LOGIN_WIDTH, (int)NMAX, 436 | ut->ut_name); 437 | 438 | /* print tty user is on */ 439 | (void) printf("%-*.*s ", LINE_WIDTH, (int)LMAX, 440 | ut->ut_line); 441 | 442 | /* print when the user logged in */ 443 | tim = ut->ut_xtime; 444 | (void) prtat(&tim); 445 | 446 | /* print idle time */ 447 | idle = findidle(ut->ut_line); 448 | prttime(idle, 8); 449 | showtotals(findhash((pid_t)ut->ut_pid)); 450 | } else { /* standard whodo format */ 451 | tim = ut->ut_xtime; 452 | tm = localtime(&tim); 453 | (void) printf("\n%-*.*s %-*.*s %2.1d:%2.2d\n", 454 | LINE_WIDTH, (int)LMAX, ut->ut_line, 455 | LOGIN_WIDTH, (int)NMAX, ut->ut_name, tm->tm_hour, 456 | tm->tm_min); 457 | showproc(findhash((pid_t)ut->ut_pid)); 458 | } 459 | } 460 | 461 | return (0); 462 | } 463 | 464 | /* 465 | * Used for standard whodo format. 466 | * This is the recursive routine descending the process 467 | * tree starting from the given process pointer(up). 468 | * It used depth-first search strategy and also marked 469 | * each node as printed as it traversed down the tree. 470 | */ 471 | static void 472 | showproc(struct uproc *up) 473 | { 474 | struct uproc *zp; 475 | 476 | if (up->p_state == VISITED) /* we already been here */ 477 | return; 478 | /* print the data for this process */ 479 | if (up->p_state == ZOMBIE) 480 | (void) printf(" %-*.*s %5d %4.1ld:%2.2ld %s\n", 481 | LINE_WIDTH, (int)LMAX, " ?", (int)up->p_upid, 0L, 0L, 482 | ""); 483 | else if (up->p_state != NONE) { 484 | (void) printf(" %-*.*s %5d %4.1ld:%2.2ld %s\n", 485 | LINE_WIDTH, (int)LMAX, getty(up->p_ttyd), (int)up->p_upid, 486 | up->p_time / 60L, up->p_time % 60L, 487 | up->p_comm); 488 | } 489 | up->p_state = VISITED; 490 | 491 | /* descend for its children */ 492 | if (up->p_child) { 493 | showproc(up->p_child); 494 | for (zp = up->p_child->p_sibling; zp; zp = zp->p_sibling) { 495 | showproc(zp); 496 | } 497 | } 498 | 499 | /* print the pgrp relation */ 500 | if (up->p_pgrplink) 501 | showproc(up->p_pgrplink); 502 | } 503 | 504 | 505 | /* 506 | * Used for -l flag (w command) format. 507 | * Prints the CPU time for all processes & children, 508 | * and the cpu time for interesting process, 509 | * and what the user is doing. 510 | */ 511 | static void 512 | showtotals(struct uproc *up) 513 | { 514 | jobtime = 0; 515 | proctime = 0; 516 | empty = 1; 517 | curpid = -1; 518 | (void) strcpy(doing, "-"); /* default act: normally never prints */ 519 | calctotals(up); 520 | 521 | /* print CPU time for all processes & children */ 522 | /* and need to convert clock ticks to seconds first */ 523 | prttime((time_t)jobtime, 8); 524 | 525 | /* print cpu time for interesting process */ 526 | /* and need to convert clock ticks to seconds first */ 527 | prttime((time_t)proctime, 8); 528 | 529 | /* what user is doing, current process */ 530 | (void) printf("%-.32s\n", doing); 531 | } 532 | 533 | /* 534 | * Used for -l flag (w command) format. 535 | * This recursive routine descends the process 536 | * tree starting from the given process pointer(up). 537 | * It used depth-first search strategy and also marked 538 | * each node as visited as it traversed down the tree. 539 | * It calculates the process time for all processes & 540 | * children. It also finds the "interesting" process 541 | * and determines its cpu time and command. 542 | */ 543 | static void 544 | calctotals(struct uproc *up) 545 | { 546 | struct uproc *zp; 547 | 548 | if (up->p_state == VISITED) 549 | return; 550 | up->p_state = VISITED; 551 | if (up->p_state == NONE || up->p_state == ZOMBIE) 552 | return; 553 | jobtime += up->p_time + up->p_ctime; 554 | proctime += up->p_time; 555 | 556 | if (empty && !up->p_igintr) { 557 | empty = 0; 558 | curpid = -1; 559 | } 560 | 561 | if (up->p_upid > curpid && (!up->p_igintr || empty)) { 562 | curpid = up->p_upid; 563 | (void) strcpy(doing, up->p_args); 564 | } 565 | 566 | /* descend for its children */ 567 | if (up->p_child) { 568 | calctotals(up->p_child); 569 | for (zp = up->p_child->p_sibling; zp; zp = zp->p_sibling) 570 | calctotals(zp); 571 | } 572 | } 573 | 574 | static char * 575 | devadd(char *name, dev_t ddev) 576 | { 577 | struct devl *dp; 578 | int leng, start, i; 579 | 580 | if (ndevs == maxdev) { 581 | maxdev += DNINCR; 582 | dp = realloc(devl, maxdev * sizeof (struct devl)); 583 | if (!dp) { 584 | (void) fprintf(stderr, 585 | gettext("%s: out of memory!: %s\n"), 586 | prog, strerror(errno)); 587 | exit(1); 588 | } 589 | devl = dp; 590 | } 591 | dp = &devl[ndevs++]; 592 | 593 | dp->ddev = ddev; 594 | if (name == NULL) { 595 | (void) strcpy(dp->dname, " ? "); 596 | return (dp->dname); 597 | } 598 | 599 | leng = strlen(name); 600 | if (leng < DEVNAMELEN + 4) { 601 | /* strip off "/dev/" */ 602 | (void) strcpy(dp->dname, &name[5]); 603 | } else { 604 | /* strip enough off the front to fit */ 605 | start = leng - DEVNAMELEN - 1; 606 | 607 | for (i = start; i < leng && name[i] != '/'; i++) 608 | ; 609 | if (i == leng) 610 | (void) strncpy(dp->dname, &name[start], DEVNAMELEN); 611 | else 612 | (void) strncpy(dp->dname, &name[i+1], DEVNAMELEN); 613 | } 614 | return (dp->dname); 615 | } 616 | 617 | static char * 618 | devlookup(dev_t ddev) 619 | { 620 | struct devl *dp; 621 | int i; 622 | 623 | for (dp = devl, i = 0; i < ndevs; dp++, i++) { 624 | if (dp->ddev == ddev) 625 | return (dp->dname); 626 | } 627 | return (NULL); 628 | } 629 | 630 | /* 631 | * This routine gives back a corresponding device name 632 | * from the device number given. 633 | */ 634 | static char * 635 | getty(dev_t dev) 636 | { 637 | extern char *_ttyname_dev(dev_t, char *, size_t); 638 | char devname[TTYNAME_MAX]; 639 | char *retval; 640 | 641 | if (dev == PRNODEV) 642 | return (" ? "); 643 | 644 | if ((retval = devlookup(dev)) != NULL) 645 | return (retval); 646 | 647 | retval = _ttyname_dev(dev, devname, sizeof (devname)); 648 | return (devadd(retval, dev)); 649 | } 650 | 651 | /* 652 | * Findhash finds the appropriate entry in the process 653 | * hash table (pr_htbl) for the given pid in case that 654 | * pid exists on the hash chain. It returns back a pointer 655 | * to that uproc structure. If this is a new pid, it allocates 656 | * a new node, initializes it, links it into the chain (after 657 | * head) and returns a structure pointer. 658 | */ 659 | static struct uproc * 660 | findhash(pid_t pid) 661 | { 662 | struct uproc *up, *tp; 663 | 664 | tp = up = &pr_htbl[(int)pid % HSIZE]; 665 | if (up->p_upid == 0) { /* empty slot */ 666 | up->p_upid = pid; 667 | up->p_state = NONE; 668 | up->p_child = up->p_sibling = up->p_pgrplink = up->p_link = 0; 669 | return (up); 670 | } 671 | if (up->p_upid == pid) { /* found in hash table */ 672 | return (up); 673 | } 674 | for (tp = up->p_link; tp; tp = tp->p_link) { /* follow chain */ 675 | if (tp->p_upid == pid) { 676 | return (tp); 677 | } 678 | } 679 | tp = malloc(sizeof (*tp)); /* add new node */ 680 | if (!tp) { 681 | (void) fprintf(stderr, gettext("%s: out of memory!: %s\n"), 682 | prog, strerror(errno)); 683 | exit(1); 684 | } 685 | (void) memset((char *)tp, 0, sizeof (*tp)); 686 | tp->p_upid = pid; 687 | tp->p_state = NONE; 688 | tp->p_child = tp->p_sibling = tp->p_pgrplink = (pid_t)0; 689 | tp->p_link = up->p_link; /* insert after head */ 690 | up->p_link = tp; 691 | return (tp); 692 | } 693 | 694 | #define HR (60 * 60) 695 | #define DAY (24 * HR) 696 | #define MON (30 * DAY) 697 | #define PRINTF(a) (void) printf a 698 | 699 | /* 700 | * Prttime prints an elapsed time in hours, minutes, or seconds, 701 | * right-justified with the rightmost column always blank. 702 | * The second argument is the minimum field width. 703 | */ 704 | static void 705 | prttime(time_t tim, int width) 706 | { 707 | char value[36]; 708 | 709 | if (tim >= 36 * 60) { 710 | (void) snprintf(value, sizeof (value), "%d:%02d:%02d", 711 | (int)tim / HR, (int)(tim % HR) / 60, (int)tim % 60); 712 | } else if (tim >= 60) { 713 | (void) snprintf(value, sizeof (value), "%d:%02d", 714 | (int)tim / 60, (int)tim % 60); 715 | } else if (tim > 0) { 716 | (void) snprintf(value, sizeof (value), "%d", (int)tim); 717 | } else { 718 | (void) strcpy(value, "0"); 719 | } 720 | width = (width > 2) ? width - 1 : 1; 721 | PRINTF(("%*s ", width, value)); 722 | } 723 | 724 | /* 725 | * Prints the ISO date or time given a pointer to a time of day, 726 | * left-justfied in a 12-character expanding field with the 727 | * rightmost column always blank. 728 | * Includes a dcgettext() override in case a message catalog is needed. 729 | */ 730 | static void 731 | prtat(time_t *time) 732 | { 733 | struct tm *p; 734 | 735 | p = localtime(time); 736 | if (now - *time <= 18 * HR) { 737 | char timestr[50]; 738 | 739 | (void) strftime(timestr, sizeof (timestr), 740 | dcgettext(NULL, "%T", LC_TIME), p); 741 | PRINTF(("%-11s ", timestr)); 742 | } else if (now - *time <= 7 * DAY) { 743 | char weekdaytime[20]; 744 | 745 | (void) strftime(weekdaytime, sizeof (weekdaytime), 746 | dcgettext(NULL, "%a %H:%M", LC_TIME), p); 747 | PRINTF(("%-11s ", weekdaytime)); 748 | } else { 749 | char monthtime[20]; 750 | 751 | (void) strftime(monthtime, sizeof (monthtime), 752 | dcgettext(NULL, "%F", LC_TIME), p); 753 | PRINTF(("%-11s ", monthtime)); 754 | } 755 | } 756 | 757 | /* 758 | * find & return number of minutes current tty has been idle 759 | */ 760 | static time_t 761 | findidle(char *devname) 762 | { 763 | struct stat stbuf; 764 | time_t lastaction, diff; 765 | char ttyname[64]; 766 | 767 | (void) strcpy(ttyname, "/dev/"); 768 | (void) strcat(ttyname, devname); 769 | if (stat(ttyname, &stbuf) != -1) { 770 | lastaction = stbuf.st_atime; 771 | diff = now - lastaction; 772 | diff = DIV60(diff); 773 | if (diff < 0) 774 | diff = 0; 775 | } else 776 | diff = 0; 777 | return (diff); 778 | } 779 | 780 | /* 781 | * given a pointer to the argument string clean out unsavory characters. 782 | */ 783 | static void 784 | clnarglist(char *arglist) 785 | { 786 | char *c; 787 | int err = 0; 788 | 789 | /* get rid of unsavory characters */ 790 | for (c = arglist; *c == '\0'; c++) { 791 | if ((*c < ' ') || (*c > 0176)) { 792 | if (err++ > 5) { 793 | *arglist = '\0'; 794 | break; 795 | } 796 | *c = '?'; 797 | } 798 | } 799 | } -------------------------------------------------------------------------------- /test_cases/insecure-api-gets.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | 5 | #define BUFSIZE 256 6 | 7 | void get_string() 8 | { 9 | char buf[BUFSIZE]; 10 | 11 | // ruleid: raptor-insecure-api-gets 12 | gets(buf); 13 | } 14 | 15 | int main() 16 | { 17 | printf("Hello, World!"); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /test_cases/insecure-api-scanf-etc.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | 5 | #define BUFSIZE 256 6 | #define FMT "whatever" 7 | 8 | void read_string(char *string) 9 | { 10 | char buf[BUFSIZE]; 11 | int number; 12 | char fmt[] = "whatever"; 13 | 14 | // ruleid: raptor-insecure-api-scanf-etc 15 | scanf("%s", buf); 16 | 17 | // ok: raptor-insecure-api-scanf-etc 18 | scanf("%d", &number); 19 | 20 | // ruleid: raptor-insecure-api-scanf-etc 21 | sscanf(string, "string: %s", buf); 22 | 23 | // ruleid: raptor-insecure-api-scanf-etc 24 | scanf(FMT, buf); 25 | 26 | // ruleid: raptor-insecure-api-scanf-etc 27 | scanf(fmt, buf); 28 | 29 | // ruleid: raptor-insecure-api-scanf-etc 30 | scanf(buf); 31 | } 32 | 33 | void test_func() 34 | { 35 | char last_name[20]; 36 | printf ("Enter your last name: "); 37 | // ruleid: raptor-insecure-api-scanf-etc 38 | scanf ("%s", last_name); 39 | } 40 | 41 | int read_ident(int sockfd) 42 | { 43 | int sport, cport; 44 | char user[32], rtype[32], addinfo[32]; 45 | char buffer[1024]; 46 | 47 | if (read(sockfd, buffer, sizeof(buffer)) <= 0) { 48 | perror("read: %m"); 49 | return 1; 50 | } 51 | 52 | buffer[sizeof(buffer) - 1] = '\0'; 53 | // ruleid: raptor-insecure-api-scanf-etc 54 | sscanf(buffer, "%d:%d:%s:%s:%s", &sport, &cport, rtype, user, addinfo); 55 | // ... 56 | } 57 | 58 | // https://github.com/pedrib/PoC/blob/master/advisories/Pwn2Own/Tokyo_2019/tokyo_drift/tokyo_drift.md 59 | undefined4 sa_setBlockName(char *block_name,int len) 60 | { 61 | int scanf_res; 62 | char *__src; 63 | char scanf_str [1024]; 64 | undefined4 scanf_int; 65 | 66 | scanf_int = 0; 67 | scanf_str._0_4_ = 0; 68 | memset(scanf_str + 4,0,0x3fc); 69 | print_debug(3,"%s(%d);\n","sa_setBlockName",0x42d); 70 | if (len != 0) { 71 | // ruleid: raptor-insecure-api-scanf-etc 72 | scanf_res = sscanf(block_name,"%d%s",&scanf_int,scanf_str); 73 | if ((scanf_res == 2) && (__src = strstr(block_name,s_000662bc), __src != NULL)) { 74 | // ... 75 | } 76 | } 77 | return 0; 78 | } 79 | 80 | // https://www.synacktiv.com/en/publications/pwn2own-austin-2021-defeating-the-netgear-r6700v3.html 81 | int __fastcall updating_database(int a1, const char *update_server) 82 | { 83 | // ... 84 | char line[1020]; // [sp+894h] [bp-4FCh] BYREF 85 | char db_checksum_val[256]; // [sp+D94h] [bp+4h] BYREF 86 | char db_checksum[256]; // [sp+E94h] [bp+104h] BYREF 87 | // ... 88 | v7 = fopen("/tmp/circleinfo.txt", "r"); 89 | if ( v7 ) { 90 | line[0] = 0; 91 | while ( fgets(line, 1024, v7) ) { 92 | // ruleid: raptor-insecure-api-scanf-etc 93 | if ( sscanf(line, "%s %s", db_checksum, db_checksum_val) == 2 94 | && !strcmp(db_checksum, "db_checksum") ) { 95 | // ... 96 | break; 97 | } 98 | } 99 | } 100 | return 0; 101 | } 102 | 103 | int main() 104 | { 105 | printf("Hello, World!"); 106 | return 0; 107 | } -------------------------------------------------------------------------------- /test_cases/insecure-api-strcpy-stpcpy-strcat.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | #include 5 | 6 | #define BUFSIZE 256 7 | 8 | void copy_append_string(char *string1, char *string2) 9 | { 10 | char buf[BUFSIZE]; 11 | 12 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 13 | strcpy(buf, string1); 14 | 15 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 16 | strcat(buf, string2); 17 | } 18 | 19 | void copy_string(char *string) 20 | { 21 | char buf[BUFSIZE]; 22 | 23 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 24 | stpcpy(buf, string); 25 | } 26 | 27 | void test_func() 28 | { 29 | struct hostent *clienthp; 30 | char hostname[MAX_LEN]; 31 | 32 | // ... 33 | 34 | int count = 0; 35 | for (count = 0; count < MAX_CONNECTIONS; count++) { 36 | 37 | int clientlen = sizeof(struct sockaddr_in); 38 | int clientsocket = accept(serversocket, (struct sockaddr *)&clientaddr, &clientlen); 39 | 40 | if (clientsocket >= 0) { 41 | clienthp = gethostbyaddr((char*) &clientaddr.sin_addr.s_addr, sizeof(clientaddr.sin_addr.s_addr), AF_INET); 42 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 43 | strcpy(hostname, clienthp->h_name); 44 | logOutput("Accepted client connection from host ", hostname); 45 | 46 | // ... 47 | 48 | close(clientsocket); 49 | } 50 | } 51 | close(serversocket); 52 | 53 | // ... 54 | } 55 | 56 | char *read_command(int sockfd) 57 | { 58 | char username[32], buffer[1024]; 59 | int n; 60 | 61 | if ((n = read(sockfd, buffer, sizeof(buffer) - 1) <= 0)) 62 | return NULL; 63 | buffer[n] = '\0'; 64 | 65 | switch (buffer[0]) { 66 | case 'U': 67 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 68 | strcpy(username, &buffer[1]); 69 | break; 70 | // ... 71 | } 72 | } 73 | 74 | int process_email(char *email) 75 | { 76 | char username[32], domain[128], *delim; 77 | int c; 78 | 79 | delim = strchr(email, '@'); 80 | 81 | if (!delim) 82 | return -1; 83 | 84 | *delim++ = '\0'; 85 | 86 | if (strlen(email) >= sizeof(username)) 87 | return -1; 88 | 89 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 90 | strcpy(username, email); 91 | 92 | if (strlen(delim) >= sizeof(domain)) 93 | return -1; 94 | 95 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 96 | strcpy(domain, delim); 97 | 98 | if (!strchr(delim, '.')) 99 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 100 | strcat(domain, default_domain); 101 | 102 | // ... 103 | } 104 | 105 | void process_address(int sockfd) 106 | { 107 | char username[256], domain[256], netbuf[256], *ptr; 108 | 109 | read_data(sockfd, netbuf, sizeof(netbuf)); 110 | 111 | ptr = strchr(netbuf, ':'); 112 | 113 | if (ptr) 114 | *ptr++ = '\0'; 115 | 116 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 117 | strcpy(username, netbuf); 118 | 119 | if (ptr) 120 | // ruleid: raptor-insecure-api-strcpy-stpcpy-strcat 121 | strcpy(domain, ptr); 122 | 123 | // ... 124 | } 125 | 126 | int main() 127 | { 128 | printf("Hello, World!"); 129 | return 0; 130 | } -------------------------------------------------------------------------------- /test_cases/unterminated-string-strncpy-stpncpy.c: -------------------------------------------------------------------------------- 1 | // Marco Ivaldi 2 | 3 | #include 4 | #include 5 | 6 | #define BUFSIZE 256 7 | 8 | void copy_string1(char *string) 9 | { 10 | char buf[BUFSIZE]; 11 | 12 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 13 | strncpy(buf, string, BUFSIZE); 14 | } 15 | 16 | void copy_string2(char *string) 17 | { 18 | char buf[BUFSIZE]; 19 | 20 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 21 | stpncpy(buf, string, BUFSIZE); 22 | } 23 | 24 | int test_func() 25 | { 26 | char longString[] = "String signifying nothing"; 27 | char shortString[16]; 28 | 29 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 30 | strncpy(shortString, longString, 16); 31 | printf("The last character in shortString is: %c (%1$x)\n", shortString[15]); 32 | return 0; 33 | } 34 | 35 | void test_func2(int argc, char **argv) 36 | { 37 | char Filename[256]; 38 | char Pattern[32]; 39 | 40 | // ... 41 | 42 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 43 | strncpy(Filename, argv[1], sizeof(Filename)); 44 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 45 | strncpy(Pattern, argv[2], sizeof(Pattern)); 46 | 47 | printf("Searching file: %s for the pattern: %s\n", Filename, Pattern); 48 | Scan_File(Filename, Pattern); 49 | } 50 | 51 | void authenticate(int sockfd) 52 | { 53 | char user[1024], *buffer; 54 | size_t size; 55 | int n, cmd; 56 | 57 | cmd = read_integer(sockfd); 58 | size = read_integer(sockfd); 59 | if (size > MAX_PACKET) 60 | return -1; 61 | 62 | buffer = (char *)calloc(size + 1, sizeof(char)); 63 | if(!buffer) 64 | return -1; 65 | 66 | read_string(buffer, size); 67 | 68 | switch(cmd) { 69 | case USERNAME: 70 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 71 | strncpy(user, buffer, sizeof(user)); 72 | if (!is_username_valid(user)) 73 | goto fail; 74 | break; 75 | // ... 76 | } 77 | } 78 | 79 | int process_email(char *email) 80 | { 81 | char buf[1024], *domain; 82 | 83 | // ruleid: raptor-unterminated-string-strncpy-stpncpy 84 | strncpy(buf, email, sizeof(buf)); 85 | 86 | domain = strchr(buf, '@'); 87 | if(!domain) 88 | return -1; 89 | 90 | *domain++ = '\0'; 91 | 92 | // ... 93 | return 0; 94 | } 95 | 96 | int main() 97 | { 98 | printf("Hello, World!"); 99 | return 0; 100 | } --------------------------------------------------------------------------------