Does the fact that ChatGPT can simply generate any algorithm mean that algorithm creation won’t be a problem for coders any longer? Can a coder suffer any negative effects from using ChatGPT?

It is simple for ChatGPT to repeat changed boilerplate that is occasionally incorrect in subtle (or perhaps not so subtle) ways.

I questioned ChatGPT on how to read a line of any length from a stream in C. It generated the following:

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. char *read_line(FILE *stream) { 
  4. char *line = NULL; 
  5. int line_size = 0; 
  6. int line_capacity = 0; 
  7. int c; 
  8. while ((c = fgetc(stream)) != EOF && c != ‘\n’) { 
  9. if (line_size >= line_capacity) { 
  10. line_capacity = line_capacity == 0 ? 64 : line_capacity * 2; 
  11. char *new_line = realloc(line, line_capacity); 
  12. if (new_line == NULL) { 
  13. free(line); 
  14. return NULL; 
  15. line = new_line; 
  16. line[line_size++] = (char)c; 
  17. if (line_size > 0 || c == ‘\n’) { 
  18. if (line_size >= line_capacity) { 
  19. line_capacity = line_capacity == 0 ? 64 : line_capacity * 2; 
  20. char *new_line = realloc(line, line_capacity); 
  21. if (new_line == NULL) { 
  22. free(line); 
  23. return NULL; 
  24. line = new_line; 
  25. line[line_size++] = ‘\0’; 
  26. return line; 
  27. }:

Here’s how the code works:

  • line is a reference to the dynamically allocated memory where the line will be stored.
  • The amount of characters in the line is indicated by line size.
  • Line capacity measures the amount of memory that is currently allocated.
  • The loop reads each character from the stream until it runs out of characters or hits a newline character.
  • The memory is reallocated to double in size if the current line size exceeds or is equal to the capacity of the allocated memory.
  • The line is extended by one character, and the line width is increased.
  • When a line or stream reaches its end while still containing characters, the null terminator is appended to the end of the line.
  • The function outputs a line pointer.
  • If there is a problem, the function returns NULL instead of a pointer to the line.

To use this function, you can pass a pointer to the stream you want to read from as an argument:

  1. int main() { 
  2. char *line = read_line(stdin); 
  3. if (line == NULL) { 
  4. fprintf(stderr, “Error: could not read line\n”); 
  5. exit(EXIT_FAILURE); 
  6. printf(“Read line: %s\n”, line); 
  7. free(line); 
  8. return 0; 

To read a line from the standard input stream, the function read line is called in this example with the argument stdin. When it is no longer required, the line is displayed to the console and the dynamically allocated memory is released using the free function.

It appears to be sensible. Not fantastic, but also not awful.

The geometric growth code may contain a signed integer overflow vulnerability. Of course, efficiency doesn’t really peak at one character at a time, does it?

I asked it if it could use fgets() to write a more effective version, and it complied. One step forward, two steps back, though:

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <string.h>  
  4. char *read_line(FILE *stream) { 
  5. char *line = NULL; 
  6. size_t line_capacity = 0; 
  7. ssize_t line_size = 0; 
  8. do { 
  9. line_capacity += BUFSIZ; 
  10. char *new_line = realloc(line, line_capacity); 
  11. if (new_line == NULL) { 
  12. free(line); 
  13. return NULL; 
  14. line = new_line; 
  15. if (fgets(line + line_size, (int)(line_capacity – line_size), stream) == NULL) { 
  16. break; 
  17.  
  18. line_size = strlen(line); 
  19. } while (line[line_size – 1] != ‘\n’); 
  20. if (line_size == 0) { 
  21. free(line); 
  22. return NULL; 
  23. line[line_size – 1] = ‘\0’; 
  24. char *new_line = realloc(line, line_size); 
  25. if (new_line == NULL) { 
  26. free(line); 
  27. return NULL; 
  28. line = new_line; 
  29. return line; 
  30. }

A few faults:

  • Our nice geometric growth allocation is gone. We now have linear growth, which is O(n2).(2).
    • But, we’ve upgraded to size_t, so any overflow there will be unsigned rather than UB inducing signed overflow.
  • When detecting how much text fgets() read in, it remeasures the entire string. Again, that’s O(n2).(2).
  • It weirdly keeps the string length in the non-standard ssize_tstrlen() returns size_t.
  • Why are we realloc’ing again at the end after we’ve successfully read the string? Just return it with a bit of extra pad at the end and avoid an extra trip through the memory allocator, an additional opportunity to fail allocation, and an extra potential memcpy().

As we are now dividing n by BUFSIZ, this effect won’t actually be felt until a moron inserts a line in your file that is roughly a megabyte in size.

But aren’t DoS attacks exactly what they live for?

When you’re through with either of these, you must free() the buffer. Your heap can become excessively fragmented as a result over time. The desire to reuse an input buffer is more frequent. No copies must be stored by you. You can just re-use the previous buffer rather than making new allocations for each line.

If you request ChatGPT to do that, it complies:

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <string.h> 
  4. #define INITIAL_LINE_CAPACITY 128 
  5. char *read_line(FILE *stream, char *line, size_t *line_capacity) { 
  6. ssize_t line_size = 0; 
  7. do { 
  8. if (fgets(line + line_size, (int)(*line_capacity – line_size), stream) == NULL) { 
  9. break; 
  10. line_size = strlen(line); 
  11. if (line[line_size – 1] == ‘\n’) { 
  12. break; 
  13. *line_capacity *= 2; 
  14. char *new_line = realloc(line, *line_capacity); 
  15. if (new_line == NULL) { 
  16. free(line); 
  17. return NULL; 
  18. line = new_line; 
  19. } while (1); 
  20. if (line_size == 0) { 
  21. return NULL; 
  22. line[line_size – 1] = ‘\0’; 
  23. char *new_line = realloc(line, line_size); 
  24. if (new_line == NULL) { 
  25. free(line); 
  26. return NULL; 
  27. line = new_line; 
  28. return line; 
  29. }

And it offers the following example code:

  1. int main() { 
  2. char *line = malloc(INITIAL_LINE_CAPACITY); 
  3. size_t line_capacity = INITIAL_LINE_CAPACITY; 
  4. while (read_line(stdin, line, &line_capacity) != NULL) { 
  5. printf(“Read line: %s\n”, line); 
  6. free(line); 
  7. return 0; 
  8. }

Once more, it appears like we are moving forward and then backward.

  • We’re back to geometric growth, which is a positive. But, that now puts this expression at risk of integer overflow: (int)(*line_capacity - line_size)
  • The argument line is passed by value while its companion line_size is passed by pointer.
    • That requires the user to reassign line on return because realloc() can move the buffer.
    • This introduces a significant opportunity to “hold it wrong” and fail to update line.
    • In fact, ChatGPT itself “holds it wrong” in its own demo code.

while (read_line(stdin, line, &line_capacity) != NULL) {

  • ChatGPT releases the buffer but fails to zero off the line capacity if realloc() fails.
  1. *line_capacity *= 2; 
  2. char *new_line = realloc(line, *line_capacity); 
  3. if (new_line == NULL) { 
  4. free(line); 
  5. return NULL; 
  6. }
  • Previous versions would bootstrap themselves from an empty allocation and zero capacity.
    • This one doesn’t, requiring the caller to do that, introducing another opportunity to “hold it wrong.”
    • ChatGPT “holds it wrong” subtly, failing to check that the initial malloc() succeeded
    • The read_line() code itself does not check whether the incoming buffer has any room or even a non-NULL pointer. That would come naturally, though, if it could bootstrap from a NULL pointer.

This genuinely resembles putting a recent college graduate through their initial code review in the workplace. Because each new version addresses one issue while breaking another, that type of code review requires a week or two of back and forth. Alternately, a mentor could go and pair-program with the novice for a little while.

I identified a few bugs in ChatGPT’s code. How many would you have managed to catch?

What percentage of those would lazy programmers who rely on ChatGPT and other code generators manage to catch?

I compared it to Sideshow Bob in a previous answer about ChatGPT, and that’s how I feel right now.

Published by Ithakuranimesh

I am a passionate programmer. I am in love with programming and computers. I have experience in Android, iOS, PHP, Python, SQL, Java, HTML, CSS, JavaScript, etc. I am passionate about learning new technologies and working on projects.

Design a site like this with WordPress.com
Get started