pacionet
pacionet

Reputation: 361

Check if uint8_t array contains string

In C language I have a uint8_t array and, after some logic, I need to cast to string and check if contains some substring like this:

uint8_t data[8] = {0xcb,0xe2,0x3d,0x96,0x55,0xfc,0xcd,0x43};
    
/// some logic
    
if(strstr((const char *)data, "000") != NULL){
    
}

Is if statement correct? it seems to return always true.

Upvotes: 3

Views: 108

Answers (2)

Lundin
Lundin

Reputation: 213960

All functions prefixed with str in the C standard library assume that you pass a null terminated string to them. If you pass something else, like in this case, you invoke undefined behavior and will get crashes or wrong results etc.

We could cook up our own (naive) function for this easily enough by iterating across the data and comparing it to the search key string with memcmp:

#include <string.h>
#include <stdint.h>

const uint8_t* memstr (const uint8_t* data, const char* key, size_t size)
{
  const uint8_t* result = NULL;
  size_t key_length = strlen(key);
  
  for(size_t i=0; i<size-key_length+1; i++)
  {
    if(memcmp(&data[i], key, key_length) == 0)
    {
      result = &data[i];
      break;
    }
  }
  return result;
}

Here the requirement is that key is a null terminated string but data could be anything. Since key is a string we can call strlen on it. We should only iterate up to the buffer size minus key length amount of bytes. So for example with 8 data bytes and a search key of "000" thats 8-3=5 bytes. But we have to check data indices 0 to 5, so a +1 on the loop condition is needed.

Complete test code:

#include <string.h>
#include <stdint.h>
#include <stdio.h>

const uint8_t* memstr (const uint8_t* data, const char* key, size_t size)
{
  const uint8_t* result = NULL;
  size_t key_length = strlen(key);

  for(size_t i=0; i<size-key_length+1; i++)
  {
    if(memcmp(&data[i], key, key_length) == 0)
    {
      result = &data[i];
      break;
    }
  }
  return result;
}


#define test_memstr(data, key)               \
  found = memstr(data, key, sizeof data);    \
  if(found)                                  \
    printf("\"%.*s\" found at index %tu\n",  \
           strlen(key), found, found-data);  \
  else                                       \
    puts("Not found");

int main (void)
{
  uint8_t data1[8]  = {0xcb,0xe2,0x3d,0x96,0x55,0xfc,0xcd,0x43};
  uint8_t data2[11] = {0x48,0x65,0x6C,0x6C,0x6F,0x20,0x57,0x6F,0x72,0x6C,0x64};
  const uint8_t* found;

  test_memstr(data1, "000");
  test_memstr(data2, "World");
  test_memstr(data2, "Hello");
}

Upvotes: 1

wohlstad
wohlstad

Reputation: 28094

As you can see in the strstr documentation, the first argument is:

str - pointer to the null-terminated byte string to examine

(emphasis is mine)

In your case data which is passed as an arugment for str is not null-terminated.

The documentation also states that:

The behavior is undefined if either str or substr is not a pointer to a null-terminated byte string.

Your code therefore invokes undefined-bahevior, meaning the standard does not guarantee the result and it's possible that the function will always return true (as well as any other behavior).

A possible solution is to make data 9 bytes and add 0x00, making it null-terminated.

Another solution (as @ChrisDodd commented) is applicable if you are using GNU libc:
Use memmem function. It works similarly to strstr, but accepts buffers length and does not require null-termination.

Upvotes: 5

Related Questions