5
\$\begingroup\$

Please review the following code:

static int md5_encode(const char *  input_md5  , char *  output  , int size)
{
  // checking pre-requisits //
  if( output == NULL || input_md5 == NULL || size == 0)
  {
   printf( "main.cpp:md5_encode() error check for the prerequisites. \n");
   exit(0);
  }

  int input_length = strlen(input_md5);

  // null terminate the output first //
  output[0] = 0;
  char * buffer [10];

  for (int i=0; i<input_length,i++)
  {
    char current_char = input_md5 [i];
    // encode it //
    strcat(output,"uc");
    sprintf(buffer,"%d",current_char);
    strcat(output,buffer);

  }
  // check for the buffer overflow condtions //
  // if something goes out of the buffer then assert //
  if(size < strlen(output) )
  {
    printf("Warning: Possible buffer-overflow\n"); 
    return 0;
  }

  // return on success //
  return 1;
}

As you can see, what this does is to encode an md5 string into ucuc format. The output buffer is allocated by the caller and its size and address are passed as parameters.

The function just writes to the output buffer with 'strcat()' without any checking. Then afterwards it checks whether 'size' is less than the amount that was written. If it exceeds the limit, then the function gives an error message.

Any more suggestions than just a warning message? How could I improve this?

\$\endgroup\$
  • \$\begingroup\$ You tagged your question as c, and the code is written in C style. Yet, curiously, the code mentions main.cpp. \$\endgroup\$ – 200_success Feb 7 '14 at 21:10
  • \$\begingroup\$ What is "ucuc" format? \$\endgroup\$ – chux Feb 7 '14 at 22:56
  • \$\begingroup\$ uc%duc%d format is the format that I taught. Because I have to store MD5 in a text .ini file. Google does not show any results. \$\endgroup\$ – sandun dhammika Feb 8 '14 at 3:45
  • \$\begingroup\$ I'd use a state machine to decode it. \$\endgroup\$ – sandun dhammika Feb 8 '14 at 3:45
  • \$\begingroup\$ Why not store a 16-character string of hexadecimal digits, the same way everyone else represents MD5 hashes? \$\endgroup\$ – 200_success Feb 8 '14 at 3:52
3
\$\begingroup\$

I'm not familiar with the "ucuc format", or what such an output format could be useful for. A Google search for md5 ucuc format shows this question itself as the only relevant hit.

What are the expected inputs? A 32-character string of hexadecimal digits? If so, the longest possible output should be 32 repetitions of uc102, corresponding to an input of ffffffffffffffff. (If the input can contain arbitrary characters, then allow for 6 bytes of output per byte of input, since there might be a negative sign.) Why not just document the requirement for the caller to pass you an output buffer capable of storing 161 bytes? Then do away with the size parameter. You could return the number of bytes of output (excluding the \0 terminator).

Printing the error messages is not advised; it contaminates the output, and side effects limit the ability to reuse your code. If you must print diagnostics, print to stderr instead. You can use the __FILE__ and __FUNCTION__ macros for the error message.

A more common way to handle null inputs is to either let it crash, or use assertions. An assertion failure message typically already includes the line number. If you use assertions, use three separate assertions, not a combined one. Calling exit() is unusual; calling exit(0) to exit successfully is even weirder.

Checking for possible buffer overflow at the end of the function is useless. The damage would already be done, and your program could very well have crashed before reaching the check.

Your use of strlen() and strcat() is inefficient, especially when done in a loop. Each of those calls is an O(n) operation, as it scans the buffer for the \0 terminator. You already know exactly where those terminators are, since you composed the output string yourself.

/**
 * Encodes the input_md5 in ucuc format, whatever that means.
 * The caller must provide an output capable of holding 5 bytes
 * per byte of input, plus 1 byte for a null terminator.
 *
 * Returns the length of the output in bytes, excluding the null
 * terminator.
 */
static int md5_encode(const char *input_md5, char *output)
{
    // Check preconditions
    assert(input_md5);
    assert(output);

    int out_offset = 0;
    for (const char *in = input_md5; *in; in++)
    {
        // Encode it. sprintf() already adds a \0 terminator.
        out_offset += sprintf(output + out_offset, "uc%d", *in);
    }

    return out_offset;
}

In my opinion, two spaces of indentation per level is too stingy for readability, and also encourages inappropriately deep nesting.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.