Skip to content

Add wrappers for malloc, realloc with a calloc-like interface#2006

Open
daviesrob wants to merge 3 commits into
samtools:developfrom
daviesrob:hts_alloc
Open

Add wrappers for malloc, realloc with a calloc-like interface#2006
daviesrob wants to merge 3 commits into
samtools:developfrom
daviesrob:hts_alloc

Conversation

@daviesrob
Copy link
Copy Markdown
Member

Help avoid bugs due to integer wrap-around when calculating sizes to pass to malloc() or realloc(). calloc() is better in this respect as it takes two parameters (number and size) and catches overflows when multiplying them together. The new interfaces provide similar functions for malloc() and realloc(), along with additional ones to handle cases that commonly occur in the code base.

All the functions are static inlines, so it should be possible for the optimiser to simplify the code in the cases where overflow cannot occur (for example, due to the size of the data types actually being passed in).

The interfaces are:

  • Saturating arithmetic

    Primitives used to build the later functions.

    • hts_add_sat2(a, b) returns (a + b) or SIZE_MAX on overflow

    • hts_add_sat3(a, b, c) returns (a + b + c) or SIZE_MAX on overflow

    • hts_prod_sat2(a, b) returns (a * b) or SIZE_MAX on overflow

  • Wrappers around malloc()

    • hts_malloc(size_t size)
      For use with the saturating arithmetic functions above. Catches over-large allocations before they get to malloc(). Prevents spurious gcc warnings about very large allocations, and allows the optimiser to convert the overflow cases to an early exit.

    • hts_malloc_p(size_t a, size_t b)
      Replaces malloc(a * b)

    • hts_malloc_ps(size_t sz, size_t a, size_t b)
      Replaces malloc(sz * (a + b))

    • hts_malloc_pse(size_t sz, size_t a, size_t b, size_t extra)
      Replaces malloc(sz * (a + b) + extra)

  • Wrappers around calloc()

    • hts_calloc(size_t size, size_t num)
      For use with the saturating arithmetic functions above. Catches over-large allocations before they get to calloc(). Prevents spurious gcc warnings about very large allocations, and allows the optimiser to convert the overflow cases to an early exit.

    • hts_calloc_ps(size_t sz, size_t a, size_t b)
      Replaces calloc(a + b, sz)

    • hts_calloc_pse(size_t sz, size_t a, size_t b, size_t extra)
      Replaces calloc(sz * (a + b) + extra, 1)

  • Wrappers around realloc()

    • hts_realloc(void *orig, size_t size)
      For use with the saturating arithmetic functions above. Catches over-large allocations before they get to realloc(). Prevents spurious gcc warnings about very large allocations, and allows the optimiser to convert the overflow cases to an early exit.

    • hts_realloc_p(void *orig, size_t a, size_t b)
      Replaces realloc(orig, a * b)

    • hts_realloc_ps(void *orig, size_t sz, size_t a, size_t b)
      Replaces realloc(orig, sz * (a + b))

    • hts_realloc_pse(void *orig, size_t sz, size_t a, size_t b, size_t extra)
      Replaces realloc(orig, sz * (a + b) + extra)

The new interfaces are applied to the existing code where appropriate. To keep the changes simple, no attempt is made here to fix issues around handling NULL returns. Such fixes will be done in a future update.

daviesrob added 3 commits May 11, 2026 10:15
Help avoid bugs due to integer wrap-around when calculating
sizes to pass to malloc() or realloc().  calloc() is better
in this respect as it takes two parameters (number and size)
and catches overflows when multiplying them together.  The
new interfaces provide similar functions for malloc() and
realloc(), along with additional ones to handle cases that
commonly occur in the code base.

All the functions are static inlines, so it should be
possible for the optimiser to simplify the code in
the cases where overflow cannot occur (for example,
due to the size of the data types actually being
passed in).

The interfaces are:

* Saturating arithmetic

  Primitives used to build the later functions.

  - hts_add_sat2(a, b) returns (a + b) or SIZE_MAX on
    overflow

  - hts_add_sat3(a, b, c) returns (a + b + c) or SIZE_MAX
    on overflow

  - hts_prod_sat2(a, b) returns (a * b) or SIZE_MAX on
    overflow

* Wrappers around malloc()

  - hts_malloc(size_t size)
    For use with the saturating arithmetic functions
    above.  Catches over-large allocations before they get
    to malloc().  Prevents spurious gcc warnings about
    very large allocations, and allows the optimiser
    to convert the overflow cases to an early exit.

  - hts_malloc_p(size_t a, size_t b)
    Replaces malloc(a * b)

  - hts_malloc_ps(size_t sz, size_t a, size_t b)
    Replaces malloc(sz * (a + b))

  - hts_malloc_pse(size_t sz, size_t a, size_t b, size_t extra)
    Replaces malloc(sz * (a + b) + extra)

* Wrappers around calloc()

  - hts_calloc(size_t size, size_t num)
    For use with the saturating arithmetic functions
    above.  Catches over-large allocations before they get
    to calloc().  Prevents spurious gcc warnings about
    very large allocations, and allows the optimiser
    to convert the overflow cases to an early exit.

  - hts_calloc_ps(size_t sz, size_t a, size_t b)
    Replaces calloc(a + b, sz)

  - hts_calloc_pse(size_t sz, size_t a, size_t b, size_t extra)
    Replaces calloc(sz * (a + b) + extra, 1)

* Wrappers around realloc()

  - hts_realloc(void *orig, size_t size)
    For use with the saturating arithmetic functions
    above.  Catches over-large allocations before they get
    to realloc().  Prevents spurious gcc warnings about
    very large allocations, and allows the optimiser
    to convert the overflow cases to an early exit.

  - hts_realloc_p(void *orig, size_t a, size_t b)
    Replaces realloc(orig, a * b)

  - hts_realloc_ps(void *orig, size_t sz, size_t a, size_t b)
    Replaces realloc(orig, sz * (a + b))

  - hts_realloc_pse(void *orig, size_t sz, size_t a, size_t b,
                    size_t extra)
    Replaces realloc(orig, sz * (a + b) + extra)
Comment thread faidx.c

// Over-allocate so there is extra space for one end-of-line sequence
buffer = (char*)malloc((size_t) end - beg + val->line_len - val->line_blen + 1);
buffer = malloc(hts_add_sat3(end - beg, val->line_len - val->line_blen, 1));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be an hts_malloc here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Comment thread realn.c

assert(bq == NULL); // bq was used above, but should now be NULL
bq = malloc(align_lqseq * 3 + lref);
bq = malloc(hts_add_sat2(hts_prod_sat2(align_lqseq, 3), lref));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here. PTRDIFF_MAX could be smaller than SIZE_MAX.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one can actually be turned into hts_malloc_pse()

Comment thread sam.c
if (cp == l->data) {
line_frag = l->data_size;
char *rp = realloc(l->data, l->alloc * 2 + 8);
char *rp = realloc(l->data, hts_add_sat2(hts_prod_sat2(l->alloc, 2), 8));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hts_realloc.

@daviesrob
Copy link
Copy Markdown
Member Author

I found a couple more instances; will fix.

Comment thread cram/cram_codecs.c
if (!new_freqs) goto nomem;
freqs = new_freqs;
lens = calloc(2*nvals, sizeof(*lens));
lens = calloc(nvals, 2 * sizeof(*lens));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hts_calloc

Comment thread cram/cram_encode.c
if (c->tags_used->n_occupied) {
int ntags = c->tags_used->n_occupied;
s->aux_block = calloc(ntags*2, sizeof(*s->aux_block));
s->aux_block = calloc(hts_prod_sat2(ntags, 2), sizeof(*s->aux_block));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hts_calloc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants