mailing list of musl libc
 help / color / mirror / code / Atom feed
* [musl] denial-of-service issue in musl’s iconv implementation
@ 2026-01-23 15:17 Richard Howe
  2026-01-23 15:40 ` Rich Felker
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Richard Howe @ 2026-01-23 15:17 UTC (permalink / raw)
  To: musl


[-- Attachment #1.1.1: Type: text/plain, Size: 1392 bytes --]

Hello,

I am reporting a denial-of-service issue in musl’s iconv implementation.
Summary

A crafted input passed to iconv() can trigger an internal assertion failure
in gconv():

../iconv/skeleton.c:745: gconv: Assertion `outbuf == outerr' failed

This causes the process to abort, resulting in a denial of service.
Affected versions

Tested on musl 1.2.5 (x86_64).
I have not tested earlier versions.
Impact

This issue allows untrusted input to reliably crash processes using iconv().
The failure occurs via an internal invariant violation and results in
abort().

When musl is rebuilt with assertions disabled (-DNDEBUG), the same input no
longer crashes and does not appear to cause memory corruption, indicating
this is a DoS issue rather than an RCE.
Reproduction

The issue is reproducible using a simple harness that invokes iconv_open()
and iconv() on attacker‑controlled input.

Steps:

   1.

   Build musl normally (assertions enabled).
   2.

   Compile the attached harness against musl.
   3.

   Run the harness with the provided input file.

The process aborts with the assertion above.

I am happy to provide:

   -

   the minimal crashing input
   -

   the reproduction harness
   -

   additional debugging information if needed

Thank you for your time.

Best regards,
Richard Howe


[image: image.png]

[-- Attachment #1.1.2: Type: text/html, Size: 2396 bytes --]

[-- Attachment #1.2: image.png --]
[-- Type: image/png, Size: 278470 bytes --]

[-- Attachment #2: id:000000,sig:06,src:000041+000001,time:165387,execs:284094,op:havoc,rep:17 --]
[-- Type: application/octet-stream, Size: 34 bytes --]

[-- Attachment #3: harness-debug.c --]
[-- Type: text/x-csrc, Size: 6377 bytes --]

#define _GNU_SOURCE
#include <iconv.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

/* ---------------- Configuration ---------------- */

#define MAX_INPUT_SIZE     (64 * 1024)
#define MAX_OUTPUT_SIZE    (128 * 1024)
#define MAX_STEPS          4096
#define MAX_CONVERSIONS    8

/* Chaos control */
#define CHAOS_PROBABILITY  10   /* % of cases entering chaos mode */

/* Musl-relevant encodings */
static const char *encodings[] = {
    "UTF-8",
    "UTF-16LE",
    "UTF-16BE",
    "UTF-32LE",
    "UTF-32BE",
    "ASCII",
    "ISO-8859-1",
    "ISO-8859-15",
    "UTF-7",
    "SHIFT_JIS",
    "EUC-JP",
    "GB18030",
};

#define ENC_COUNT (sizeof(encodings) / sizeof(encodings[0]))

/* ---------------- Utilities ---------------- */

static inline uint16_t rd16(const uint8_t *p) {
    return ((uint16_t)p[0] << 8) | p[1];
}

static ssize_t read_file(const char *path, uint8_t *buf, size_t max) {
    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }

    ssize_t total = 0;
    while (total < (ssize_t)max) {
        ssize_t r = read(fd, buf + total, max - total);
        if (r <= 0)
            break;
        total += r;
    }

    close(fd);
    return total;
}

/* ---------------- Main Harness ---------------- */

int main(int argc, char **argv) {

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
        return 1;
    }

    static uint8_t input[MAX_INPUT_SIZE];
    static uint8_t output[MAX_OUTPUT_SIZE];

    ssize_t len = read_file(argv[1], input, sizeof(input));
    if (len <= 8)
        return 0;

    const uint8_t *p = input;
    const uint8_t *end = input + len;

    uint8_t gflags = p[0];
    uint8_t conv_count = (p[1] % MAX_CONVERSIONS) + 1;
    uint16_t seed = rd16(p + 2);

    /* Chaos mode is rare and explicit */
    int chaos = ((seed % 100) < CHAOS_PROBABILITY);

    p += 4;

    for (uint8_t c = 0; c < conv_count && p + 6 < end; c++) {

        uint8_t flags   = p[0];
        uint8_t src_sel = p[1] % ENC_COUNT;
        uint8_t dst_sel = p[2] % ENC_COUNT;
        uint16_t in_len = rd16(p + 3);

        p += 5;
        if (p + in_len > end)
            in_len = end - p;

        /* ---------------- Encoding name fuzzing ---------------- */

        const char *src = encodings[src_sel];
        const char *dst = encodings[dst_sel];

        if (chaos && (flags & 0x80) && p + 1 < end) {
            src = (const char *)p;
        }

        if (chaos && (flags & 0x40) && p + 2 < end) {
            dst = (const char *)(p + 1);
        }

        iconv_t cd = iconv_open(dst, src);
        if (cd == (iconv_t)-1) {
            p += in_len;
            continue;
        }

        char *in_ptr = (char *)p;
        size_t in_left = in_len;

        char *out_base = (char *)output;
        size_t out_cap = sizeof(output);

        /* ---------------- Aliasing abuse ---------------- */

        if (chaos && (flags & 0x20) && in_left > 8) {
            out_base = in_ptr;
            out_cap  = in_left;
        }

        char *out_ptr = out_base;
        size_t out_left = out_cap;

        size_t steps = 0;
        size_t last_in_left = in_left;

        /* ---------------- Conversion loop ---------------- */

        while (in_left > 0 &&
              (chaos || steps++ < MAX_STEPS)) {

            size_t chunk;
            switch (flags & 0x03) {
                case 0: chunk = 1; break;
                case 1: chunk = 2; break;
                case 2: chunk = 4; break;
                default: chunk = (seed % 8) + 1; break;
            }

            if (chunk > in_left)
                chunk = in_left;

            char *tin = in_ptr;
            size_t tin_left = chunk;

            char *tout = out_ptr;
            size_t tout_left = out_left;

            size_t r = iconv(cd, &tin, &tin_left, &tout, &tout_left);

            size_t consumed = chunk - tin_left;
            in_ptr  += consumed;
            in_left -= consumed;

            out_ptr  = tout;
            out_left = tout_left;

            if (r == (size_t)-1) {

                if (errno == E2BIG) {
                    if (flags & 0x04) {
                        out_ptr = out_base;
                        out_left = out_cap;
                    } else if (!chaos) {
                        break;
                    }
                } else if (errno == EILSEQ || errno == EINVAL) {

                    if (flags & 0x08) {
                        char *d = out_ptr;
                        size_t dl = out_left;
                        iconv(cd, NULL, NULL, &d, &dl);
                        out_ptr = d;
                        out_left = dl;
                    }

                    if (in_left > 0) {
                        in_ptr++;
                        in_left--;
                    }

                    if (!chaos)
                        break;
                } else {
                    break;
                }
            }

            /* ---------------- Progress guard ---------------- */

            if (!chaos) {
                if (in_left >= last_in_left)
                    break;
                last_in_left = in_left;
            }

            if ((flags & 0x10) && (steps & 0x7) == 0) {
                char *d = out_ptr;
                size_t dl = out_left;
                iconv(cd, NULL, NULL, &d, &dl);
                out_ptr = d;
                out_left = dl;
            }

            if (flags & 0x20) {
                char *zin = in_ptr;
                size_t zleft = 0;
                iconv(cd, &zin, &zleft, &out_ptr, &out_left);
            }

            if (out_left == 0)
                break;
        }

        /* ---------------- Final flush ---------------- */

        if (flags & 0x40) {
            char *d = out_ptr;
            size_t dl = out_left;
            iconv(cd, NULL, NULL, &d, &dl);
        }

        iconv_close(cd);
        p += in_len;
    }

    /* Global misuse */
    if (gflags & 0x80) {
        iconv_t cd = iconv_open("UTF-8", "UTF-8");
        if (cd != (iconv_t)-1) {
            char *d = (char *)output;
            size_t dl = sizeof(output);
            iconv(cd, NULL, NULL, &d, &dl);
            iconv_close(cd);
        }
    }

    return 0;
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [musl] denial-of-service issue in musl’s iconv implementation
  2026-01-23 15:17 [musl] denial-of-service issue in musl’s iconv implementation Richard Howe
@ 2026-01-23 15:40 ` Rich Felker
  2026-01-23 15:49   ` Richard Howe
  2026-01-23 18:11 ` Rich Felker
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Rich Felker @ 2026-01-23 15:40 UTC (permalink / raw)
  To: Richard Howe; +Cc: musl

On Fri, Jan 23, 2026 at 10:17:51AM -0500, Richard Howe wrote:
> Hello,
> 
> I am reporting a denial-of-service issue in musl’s iconv implementation.
> Summary

Is this distinct from CVE-2025-26519?

https://www.openwall.com/lists/musl/2025/02/13/1



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [musl] denial-of-service issue in musl’s iconv implementation
  2026-01-23 15:40 ` Rich Felker
@ 2026-01-23 15:49   ` Richard Howe
  0 siblings, 0 replies; 7+ messages in thread
From: Richard Howe @ 2026-01-23 15:49 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 1153 bytes --]

Yes it is.

The bug that I discovered is triggered by a specially crafted input to
iconv(), which causes an assertion failure in iconv/skeleton.c (outbuf ==
outerr). The application terminates with a controlled abort when processing
this input. The resulting impact is DoS only. No code execution or memory
corruption beyond the assertion is observed.

CVE-2025-26519 involves memory corruption in iconv() that can lead to *remote
code execution*. This bug bypasses musl's assertion checks (or exists in a
completely separate code path) resulting in memory corruption / code
execution. The triggering input, mechanism, and potential impact are all
different. Consequently, this bug warrants a separate CVE entry focused on
process termination (DoS) rather than code execution.


On Fri, Jan 23, 2026 at 10:40 AM Rich Felker <dalias@libc.org> wrote:

> On Fri, Jan 23, 2026 at 10:17:51AM -0500, Richard Howe wrote:
> > Hello,
> >
> > I am reporting a denial-of-service issue in musl’s iconv implementation.
> > Summary
>
> Is this distinct from CVE-2025-26519?
>
> https://www.openwall.com/lists/musl/2025/02/13/1
>
>
>

[-- Attachment #2: Type: text/html, Size: 1659 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [musl] denial-of-service issue in musl’s iconv implementation
  2026-01-23 15:17 [musl] denial-of-service issue in musl’s iconv implementation Richard Howe
  2026-01-23 15:40 ` Rich Felker
@ 2026-01-23 18:11 ` Rich Felker
  2026-01-23 19:14 ` Florian Weimer
  2026-01-24  1:53 ` Rich Felker
  3 siblings, 0 replies; 7+ messages in thread
From: Rich Felker @ 2026-01-23 18:11 UTC (permalink / raw)
  To: Richard Howe; +Cc: musl

On Fri, Jan 23, 2026 at 10:17:51AM -0500, Richard Howe wrote:
> Hello,
> 
> I am reporting a denial-of-service issue in musl’s iconv implementation.
> Summary
> 
> A crafted input passed to iconv() can trigger an internal assertion failure
> in gconv():
> 
> ../iconv/skeleton.c:745: gconv: Assertion `outbuf == outerr' failed
> 
> This causes the process to abort, resulting in a denial of service.
> Affected versions
> 
> Tested on musl 1.2.5 (x86_64).
> I have not tested earlier versions.
> Impact
> 
> This issue allows untrusted input to reliably crash processes using iconv().
> The failure occurs via an internal invariant violation and results in
> abort().
> 
> When musl is rebuilt with assertions disabled (-DNDEBUG), the same input no
> longer crashes and does not appear to cause memory corruption, indicating
> this is a DoS issue rather than an RCE.
> Reproduction
> 
> The issue is reproducible using a simple harness that invokes iconv_open()
> and iconv() on attacker‑controlled input.
> 
> Steps:
> 
>    1.
> 
>    Build musl normally (assertions enabled).
>    2.
> 
>    Compile the attached harness against musl.
>    3.
> 
>    Run the harness with the provided input file.
> 
> The process aborts with the assertion above.
> 
> I am happy to provide:
> 
>    -
> 
>    the minimal crashing input
>    -
> 
>    the reproduction harness
>    -
> 
>    additional debugging information if needed
> 
> Thank you for your time.
> 
> Best regards,
> Richard Howe
> 
> 
> [image: image.png]

The attached png is clearly showing a backtrace of a glibc-linked
program not a musl-linked one. Can you please clarify.

Rich

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [musl] denial-of-service issue in musl’s iconv implementation
  2026-01-23 15:17 [musl] denial-of-service issue in musl’s iconv implementation Richard Howe
  2026-01-23 15:40 ` Rich Felker
  2026-01-23 18:11 ` Rich Felker
@ 2026-01-23 19:14 ` Florian Weimer
  2026-01-23 19:46   ` Richard Howe
  2026-01-24  1:53 ` Rich Felker
  3 siblings, 1 reply; 7+ messages in thread
From: Florian Weimer @ 2026-01-23 19:14 UTC (permalink / raw)
  To: Richard Howe; +Cc: musl

* Richard Howe:

> Hello,
>
> I am reporting a denial-of-service issue in musl’s iconv implementation.
>
> Summary
>
> A crafted input passed to iconv() can trigger an internal assertion failure in gconv():
>
> ../iconv/skeleton.c:745: gconv: Assertion `outbuf == outerr' failed

This happens because you call iconv with input and output buffers that
overlap:

#9  0x0000000000400923 in main (argc=2, argv=0x7fffffffdd48) at harness-debug.c:164
164	            size_t r = iconv(cd, &tin, &tin_left, &tout, &tout_left);
(gdb) print tin
$1 = 0x40311a <input+26> ""
(gdb) print tin_left
$2 = 3
(gdb) print tout
$3 = 0x403119 <input+25> "A"

The glibc implementation does not support that.  I'm not sure if that's
actually a bug.  I couldn't find documentation discussing overlapping
buffers (beyond the restrict keyword, which I find a bit iffy).

Thanks,
Florian


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [musl] denial-of-service issue in musl’s iconv implementation
  2026-01-23 19:14 ` Florian Weimer
@ 2026-01-23 19:46   ` Richard Howe
  0 siblings, 0 replies; 7+ messages in thread
From: Richard Howe @ 2026-01-23 19:46 UTC (permalink / raw)
  To: Florian Weimer; +Cc: musl

[-- Attachment #1: Type: text/plain, Size: 1211 bytes --]

Got it - thank you for confirming. My harness does allow for overlapping
input/output buffers, which violates the restrict contract so that explains
the abort

On Fri, Jan 23, 2026 at 2:14 PM Florian Weimer <fweimer@redhat.com> wrote:

> * Richard Howe:
>
> > Hello,
> >
> > I am reporting a denial-of-service issue in musl’s iconv implementation.
> >
> > Summary
> >
> > A crafted input passed to iconv() can trigger an internal assertion
> failure in gconv():
> >
> > ../iconv/skeleton.c:745: gconv: Assertion `outbuf == outerr' failed
>
> This happens because you call iconv with input and output buffers that
> overlap:
>
> #9  0x0000000000400923 in main (argc=2, argv=0x7fffffffdd48) at
> harness-debug.c:164
> 164                 size_t r = iconv(cd, &tin, &tin_left, &tout,
> &tout_left);
> (gdb) print tin
> $1 = 0x40311a <input+26> ""
> (gdb) print tin_left
> $2 = 3
> (gdb) print tout
> $3 = 0x403119 <input+25> "A"
>
> The glibc implementation does not support that.  I'm not sure if that's
> actually a bug.  I couldn't find documentation discussing overlapping
> buffers (beyond the restrict keyword, which I find a bit iffy).
>
> Thanks,
> Florian
>
>

[-- Attachment #2: Type: text/html, Size: 1677 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [musl] denial-of-service issue in musl’s iconv implementation
  2026-01-23 15:17 [musl] denial-of-service issue in musl’s iconv implementation Richard Howe
                   ` (2 preceding siblings ...)
  2026-01-23 19:14 ` Florian Weimer
@ 2026-01-24  1:53 ` Rich Felker
  3 siblings, 0 replies; 7+ messages in thread
From: Rich Felker @ 2026-01-24  1:53 UTC (permalink / raw)
  To: Richard Howe; +Cc: musl

On Fri, Jan 23, 2026 at 10:17:51AM -0500, Richard Howe wrote:
> Hello,
> 
> I am reporting a denial-of-service issue in musl’s iconv implementation.
> Summary
> 
> A crafted input passed to iconv() can trigger an internal assertion failure
> in gconv():
> 
> ../iconv/skeleton.c:745: gconv: Assertion `outbuf == outerr' failed
> 
> This causes the process to abort, resulting in a denial of service.
> Affected versions
> 
> Tested on musl 1.2.5 (x86_64).
> I have not tested earlier versions.
> Impact
> 
> This issue allows untrusted input to reliably crash processes using iconv().
> The failure occurs via an internal invariant violation and results in
> abort().
> 
> When musl is rebuilt with assertions disabled (-DNDEBUG), the same input no
> longer crashes and does not appear to cause memory corruption, indicating
> this is a DoS issue rather than an RCE.
> Reproduction
> 
> The issue is reproducible using a simple harness that invokes iconv_open()
> and iconv() on attacker‑controlled input.
> 
> Steps:
> 
>    1.
> 
>    Build musl normally (assertions enabled).
>    2.
> 
>    Compile the attached harness against musl.
>    3.
> 
>    Run the harness with the provided input file.
> 
> The process aborts with the assertion above.
> 
> I am happy to provide:
> 
>    -
> 
>    the minimal crashing input
>    -
> 
>    the reproduction harness
>    -
> 
>    additional debugging information if needed
> 
> Thank you for your time.

It was raised by some folks on our IRC channel that a lot of the above
text looks like it may have been machine-generated LLM-slop. In
particular, a lot of the statements in it don't seem to apply to musl
at all, and the numbered/bulleted lists fit format typical of such
slop. If this assessment is correct, for future reference, please do
not submit reports of this form. While it hasn't been published
anywhere as official policy, our draft "AI policy" is proposed and
discussed in this thread:

https://www.openwall.com/lists/musl/2024/10/19/3


Rich

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-01-24  1:54 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-23 15:17 [musl] denial-of-service issue in musl’s iconv implementation Richard Howe
2026-01-23 15:40 ` Rich Felker
2026-01-23 15:49   ` Richard Howe
2026-01-23 18:11 ` Rich Felker
2026-01-23 19:14 ` Florian Weimer
2026-01-23 19:46   ` Richard Howe
2026-01-24  1:53 ` Rich Felker

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).