Fantastic exchange from DConf
Patrick Schluter via Digitalmars-d
digitalmars-d at puremagic.com
Tue May 9 01:18:09 PDT 2017
On Tuesday, 9 May 2017 at 06:15:12 UTC, H. S. Teoh wrote:
>
> int my_func(mytype_t *input, outbuf_t *output_buf,
> char *buffer, int size)
> {
> /* Typical lazy way of null-checking (that will blow up
> * later) */
> myhandle_t *h = input ? input->handle : 0;
> writer_t *w = output_buf ? output_buf->writer : 0;
> char *block = (char *)malloc(size);
Hey, you've been outed as a C++ programmer. A real C programmer
never casts a void *. In that specific case, casting away the
malloc() return can mask a nasty bug. If you have forgotten to
include the header declaring the function, the compiler would
assume an int returning function and the cast would suppress the
righteous warning message of the compiler. On 64 bit machines the
returned pointer would be truncated to the lower half.
Unfortunately on Linux, as the heap starts in the lower 4 GiB of
address space, the code would run for a long time before it
crashed. On Solaris-SPARC it would crash directly as binaries are
loaded address 0x1_0000_0000 of the address space.
> FILE *fp;
> int i;
>
> if (!buffer)
> return -1; /* typical useless error return code */
> /* (also, memory leak) */
>
> if (h->control_block) { /* oops, possible null deref */
> fp = fopen("blah", "w");
> if (!fp)
> return -1; /* oops, memory leak */
> }
> if (w->buffered) { /* oops, possible null deref */
> strncpy(buffer, input->data, size); /* oops, unterminated
> string */
> if (w->write(buffer, size) != 0)
> /* hmm, is 0 the error status, or is it -1? */
> /* also, what if w->write == null? */
Or is it inspired by fwrite, which returns the number of written
records? In that case 0 return might be an error or not, depends
on size.
> {
> return -1; /* oops, memory leak AND file
> descriptor leak */
> }
> }
> for (i = 0; i <= input->size; i++) { /* oops, off-by-1 error
> */
> ... /* more nauseating nasty stuff here */
> if (error)
> goto EXIT;
> ... /* ad nauseum */
> }
> EXIT:
> if (fp) fclose(fp); /* oops, uninitialized ptr deref */
Worse, you didn't check the return of fclose() on writing FILE.
fclose() can fail if the disk was full. As the FILE is buffered,
the last fwrite might not have flushed it yet. So it is the
fclose() that will try to write the last block and that can fail,
but the app wouldn't be able to even report it.
> free(block);
>
> /* Typical lazy way of evading more tedious `if
> * (func(...) == error) goto EXIT;` style code, which
> * ends up being even more error-prone */
> return error ? -1 : w->num_bytes_written();
> /* oops, what if w or w->num_bytes_written is
> * null? */
> }
>
More information about the Digitalmars-d
mailing list