简体   繁体   中英

Is this substring safe in C?

I need to extract a substring from a string with different length. I would like to avoid using malloc.

Is this safe?

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

int main()
{   
    const char *PATTERN1 = "<Abcd.";
    const char *PATTERN2 = ">";
    const char *PATTERN3 = "<Fghi.";
    char *begin, *end;

    int len;
    char result[50];
    // lenght of toFind could be different i.e. "<Abcd.toGet>" or "<Abcd.toGettoGettoGet>" always less than 50
    char *toFind[50] = {"<Abcd.toGettoGet>","<Abcd.toGettoGetoGet>","<Abcd.toGet>"}; 
    
    int element = 3;
    int i = 0;
    for (i = 0; i < element ; i++) {

    begin = strstr(toFind[i], PATTERN1);
    printf("Begin: %s \n", begin); 
    
    if (begin == NULL) {
        perror("Null begin\n");
    }
    
    begin += strlen(PATTERN1);
    printf("Begin2: %s \n", begin);
    end = strstr(begin, PATTERN2);
    printf("End: %s \n", end); 
    
    if (end == NULL) {
        perror("Null end\n");
    }
    len = end - begin;
    printf("Len: %d \n", len); 

    strncpy(result, begin, len);
    result[len] = '\0';
    
    printf("Result = %s\n",result);
    
    sleep(5);
    }
        
    return 0;
}

I'm wondering if this could be a correct and safe way to do it in order to avoid segmentation fault, or memory corruption/leak. Especially how *begin and *end it is initialized.

There are a few issues that you need to fix.

  1. Don't just carry on when begin or end is NULL.

You should add an else part where you place the code that follows. Example:

if (begin == NULL) {
    perror("Null begin\n");
} else {
    begin += strlen(PATTERN1);
    ...
    ...
    if (end == NULL) {
        perror("Null end\n");
    } else {
        len = end - begin;
        ...
        ...
    }
}
  1. Take of of writing result out of bounds.

The result array is fixed-size but len can get any size. So using strncpy(result, begin, len); will not protect you from out of bounds write. Add a check before using strncpy

  1. Don't print strings if strstr returns NULL.

ie

begin = strstr(toFind[i], PATTERN1);
printf("Begin: %s \n", begin);   // Check for NULL **before** printing

No, it's terrible.

  1. You use the result of strstr before checking for NULL. This is a bug.

  2. You don't check that len < sizeof(result) , so the strncpy is dangerous. This is a bug.

    Using strncpy_s would be safer if it were both available and well-implemented:

     errno_t error = strncpy_s(result, sizeof(result), begin, len);

    Apparently it isn't, so you need to correctly calculate the actual size yourself:

     size_t copylen = min(sizeof(result)-1, len); strncpy(result, begin, copylen); result[copylen] = '\\0';

    (observe how easy it is to get off-by-one errors with this sort of code, and don't just take my word for it: check and test it yourself).

If you want to wrap this into a function, you can't use a local array variable and return it.

The only easy way to fetch a substring with no allocation is to actually return the (pointer,length) or (original pointer,offset,length) tuples.

Otherwise, you need the caller to pass in a result array (and the correct size!), and this just pushes the allocation elsewhere.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM