Hi David,

It was a wonderful experience past discovering test cases for the
strnlen(), and I have the test file ready in
gcc/testsuite/gcc.dg/analyzer/strnlen-1.c .

I understood the implementation from the strlen-1.c and strcpy-1.c and with
their reference implemented noinline wrappers for each test case (so that
the calls remain clean) and functions like test_string, test_uninitialized,
test_partially_initialized. test_unterminated,
test_array_initialization_implicit_length.

Also, test like test_null I saw it in the TODO list of strlen() and tried
to implement it in this, I have a question should I keep that test_null
case there or not?

Also, added test_abstract to check for the returned length should be l < n.
I also checked the warnings sentence framing, some were taken with
reference to those files and the dg message "argument 1 NULL where
non-null..." was verified by taking reference to fanalyzer output of a
strlen() with null pointer reference.

I have also tried to understood the what? behind the procedures step of
kf_strnlen() that I shall implement from the kf_strncpy because it uses
logic of bifurcation, handles non-terminated strings very well.

steps I will follow implementing strneln() if you agree with this method:
1) It check for potential terminator without emitting warning,
2) Splits analysis into two paths, one where terminator is found (L <= n)
and one where the limit is reached (L == n)
3) It will use the analyzer logic for poisoned(uninitialized) bytes and out
of bounds reads.

I want to know if there's any helper function for the same? I have checked
all call-detail.h, region-model.cc but theres only one accounting for,
check_for_null_terminated_string.arg which is unbounded if in buffer \0 is
not mentioned.

The testsuite code is attached below and looking forward for your guidance
on the same.

Your's Sincerely,
Saish Kambali.


------------------sTART-------------------------------------

#include "analyzer-decls.h"

typedef __SIZE_TYPE__ size_t;

/* noinline wrapper of test_string */
static size_t __attribute__((noinline))
call_strnlen_1 (const char *p, size_t n)
{
    return __builtin_strnlen (p, n);
}

void test_string (void)
{
    /* case 1: n is greater than string length */
    __analyzer_eval (call_strnlen_1 ("abc", 10) == 3); /* { dg-warning
"TRUE" } */

    /* case 2: n is smaller than the string length */
    __analyzer_eval (call_strnlen_1 ("abc", 2) == 2); /* { dg-warning
"TRUE" } */

    /* case 3: n is exactly the string length */
    __analyzer_eval (call_strnlen_1 ("abc", 3) == 3); /* { dg-warning
"TRUE" } */
}

/* checks if pointer is NULL */
void test_null (size_t n)
{
    __builtin_strnlen (NULL, n); /* { dg-warning "use of NULL string
pointer" } */
    /* { dg-message "argument 1 NULL where non-null expected" "event" {
target *-*-* } .-1 } */
}

/* buffer declared but not initialized yet, warning if read unitialized
values */
void test_uninitialized (size_t n)
{
    char buf[16];
    if(n > 0)
    {
        __builtin_strnlen (buf, n); /* { dg-warning "use of uninitialized
value 'buf\\\[0\\\]'" } */
    }
}

/* buffer is declared and partially initialized, warning if read
unitialized values */
void test_partially_initialized (void)
{
  char buf[16];
  buf[0] = 'a';
  __builtin_strnlen (buf, 2); /* { dg-warning "use of uninitialized value
'buf\\\[1\\\]'" } */
}

/* noinline wrapper of test_unterminated case 1 */
static size_t __attribute__((noinline))
call_strnlen_2 (const char *p, size_t n)
{
    return __builtin_strnlen (p, n);
}

/* noinline wrapper of test_unterminated case 2 */
static size_t __attribute__((noinline))
call_strnlen_3 (const char *p, size_t n)
{
    return __builtin_strnlen (p, n); /* { dg-warning "stack-based buffer
over-read" } */
}

void test_unterminated (void)
{
    const char buf[3] = {'a', 'b', 'c'};

    /*case 1: SAFE as strnlen stops at n=3, so never looked for \0*/
    size_t result = call_strnlen_2 (buf, 3);
    __analyzer_eval (result == 3); /* { dg-warning "TRUE" } */

    /*case 2: Unsafe as n=4 forces the analyzer to look past the 3 bytes
buffer */
    call_strnlen_3 (buf, 4);
    /* { dg-message "while looking for null terminator for argument 1
\\(&buf\\) of 'strnlen'" "event" { target *-*-* } .-1 } */
}

/* noinline wrapper of test_max_n */
static size_t __attribute__((noinline))
call_strnlen_4 (const char *p, size_t n)
{
    return __builtin_strnlen (p, n);
}

/* for abstraction, assures the length returned l < n */
void test_abstract (const char *s, size_t n)
{
    if (s == NULL)
        return;
    size_t length = call_strnlen_4 (s, n);

    __analyzer_eval (length <= n); /* { dg-warning "TRUE" } */
}

/* noinline wrapper of test_array_initialization, case: true */
static size_t __attribute__((noinline))
call_strnlen_5 (const char *p, size_t n)
{
    return __builtin_strnlen (p, n);
}

/* noinline wrapper of test_array_initialized, case: unknown */
static size_t __attribute__((noinline))
call_strnlen_5a (const char *p, size_t n)
{
    return __builtin_strnlen (p, n); /* { dg-warning "stack-based buffer
over-read" } */
}

/* pointer arithmetic can be performed; with warnings for over reading
buffer */
void test_array_initialization_implicit_length (void)
{
    const char buf[] = "abc";

    __analyzer_eval (call_strnlen_5 (buf, 10) == 3); /* { dg-warning "TRUE"
} */
    __analyzer_eval (call_strnlen_5 (buf + 2, 3) == 1); /* { dg-warning
"TRUE" } */
    __analyzer_eval (call_strnlen_5 (buf + 3, 3) == 0); /* { dg-warning
"TRUE" } */
    __analyzer_eval (call_strnlen_5a (buf + 4, 3) == 0); /* { dg-warning
"UNKNOWN" } */
}


-------------END------------

Reply via email to