GNU bug report logs -
#13530
head: memory exhausted when printing all from stdin but last P/E bytes
Previous Next
Reported by: Lei Zhang <lei.zhang <at> uwaterloo.ca>
Date: Wed, 23 Jan 2013 02:43:01 UTC
Severity: normal
Done: Pádraig Brady <P <at> draigBrady.com>
Bug is archived. No further changes may be made.
Full log
View this message in rfc822 format
On 01/23/2013 02:32 AM, Lei Zhang wrote:
> Hi All,
>
> We found a bug in the `head' program of coreutils 8.20:
>
> Invoking `head -c -P' or `head -c -E' will cause memory exhaustion.
>
> However, smaller units (e.g., b, K, M) work fine; bigger units (e.g., Z, Y)
> fail properly
> (by outputing "number of bytes is so large that it is not representable").
> And `-n' also
> works fine.
>
> A bit dig reveals that the bug is introduced at line 322 of head.c
> (coreutils 8.20):
>
> 204: elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t
> n_elide_0)
> 205: {
> 322: b = xcalloc (n_bufs, sizeof *b); /** this statement fails **/
> 398: }
>
> We also examined n_elide and n_bufs before that statement. Actually, they
> are very
> large:
>
> n_elide: 1125899906842624
> n_bufs: 137438953474
>
> According to the following comment in the source file:
>
>> CAUTION: do not fail (out of memory) when asked to elide
>> a ridiculous amount, but when given only a small input. */
>
> We think this is a bug and bring this issue to your attention. Thanks!
There is the argument that we _should_ allocate
everything up front to indicate immediately
that the system can't (currently) support the requested operation,
but given the 'currently' caveat above I guess it's
more general to fail when we actually run out of mem?
So to do that we can either realloc our pointer array
in chunks or use the simpler approach in the patch below,
and take advantage of kernel overcommit strategies.
(calloc is problematic for overcommit as zeroing the
memory fully allocates it to the process).
The caveat with that though is that it's dependent
on the overcommit config for the kernel and possibly
current memory conditions.
thanks,
Pádraig.
diff --git a/src/head.c b/src/head.c
index cf84a95..909be04 100644
--- a/src/head.c
+++ b/src/head.c
@@ -197,7 +197,7 @@ copy_fd (int src_fd, FILE *o_stream, uintmax_t n_bytes)
return COPY_FD_OK;
}
-/* Print all but the last N_ELIDE lines from the input available via
+/* Print all but the last N_ELIDE bytes from the input available via
the non-seekable file descriptor FD. Return true upon success.
Give a diagnostic and return false upon error. */
static bool
@@ -314,18 +314,22 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
size_t n_read;
bool buffered_enough;
size_t i, i_next;
+ size_t n_alloc = 0;
char **b;
/* Round n_elide up to a multiple of READ_BUFSIZE. */
size_t rem = READ_BUFSIZE - (n_elide % READ_BUFSIZE);
size_t n_elide_round = n_elide + rem;
size_t n_bufs = n_elide_round / READ_BUFSIZE + 1;
- b = xcalloc (n_bufs, sizeof *b);
+ b = xnmalloc (n_bufs, sizeof *b);
buffered_enough = false;
for (i = 0, i_next = 1; !eof; i = i_next, i_next = (i_next + 1) % n_bufs)
{
- if (b[i] == NULL)
- b[i] = xmalloc (READ_BUFSIZE);
+ if (! buffered_enough)
+ {
+ b[i] = xmalloc (READ_BUFSIZE);
+ n_alloc = i + 1;
+ }
n_read = full_read (fd, b[i], READ_BUFSIZE);
if (n_read < READ_BUFSIZE)
{
@@ -389,7 +393,7 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
}
free_mem:
- for (i = 0; i < n_bufs; i++)
+ for (i = 0; i < n_alloc; i++)
free (b[i]);
free (b);
This bug report was last modified 12 years and 42 days ago.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.