VYPR
Low severity3.3NVD Advisory· Published May 25, 2026· Updated May 26, 2026

CVE-2026-9504

CVE-2026-9504

Description

A weakness has been identified in GNU LibreDWG up to 0.14. Affected is the function bit_convert_TU of the file programs/dwggrep.c of the component Dwggrep Utility. This manipulation causes out-of-bounds read. The attack needs to be launched locally. The exploit has been made available to the public and could be used for attacks. Patch name: be996bf2178a40e98720f18c2414815d244413db. Applying a patch is the recommended action to fix this issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

An out-of-bounds heap read in GNU LibreDWG up to 0.14 in the dwggrep utility's bit_convert_TU function allows local information disclosure or crash.

Vulnerability

A heap-buffer-overflow out-of-bounds read exists in the bit_convert_TU function of GNU LibreDWG, affecting versions up to 0.14. The vulnerable code path is triggered in the dwggrep utility when processing a malformed DWG file, specifically during the handling of LTYPE object data. The issue resides in the file programs/dwggrep.c and was confirmed via AddressSanitizer. The official patch is commit be996bf2178a40e98720f18c2414815d244413db [1][4].

Exploitation

An attacker must be able to supply a specially crafted DWG file and execute dwggrep locally against that file. No network access or authentication is required; the attack is initiated by having the victim (or an automated process) run dwggrep on the attacker-controlled input. The publicly available proof-of-concept file can trigger the overflow [1][3]. The crash occurs during the processing of LTYPE object dash strings, where bit_convert_TU reads beyond the allocated heap buffer [4].

Impact

A successful read of out-of-bounds heap memory can lead to disclosure of sensitive data present in heap memory adjacent to the buffer. In a debug or production environment, the read may also cause an application crash (denial of service). The attacker gains no code execution or privilege escalation from this vulnerability alone; the impact is limited to information disclosure and crash [1].

Mitigation

The fix is available in commit be996bf2178a40e98720f18c2414815d244413db of the LibreDWG repository [4]. Users should update to a version containing this patch as soon as possible. No known workarounds exist for unpatched versions. The CVE has a CVSS v3.1 score of 3.3 (Low) and is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 26, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • LibreDWG/Libredwgreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: <=0.14

Patches

1
be996bf2178a

Fix heap-buffer-overflow in bit_convert_TU via dwggrep LTYPE search (GH #1246)

https://github.com/libredwg/libredwgReini UrbanMay 16, 2026via nvd-ref
3 files changed · +131 3
  • programs/dwggrep.c+25 1 modified
    @@ -517,7 +517,31 @@ match_LTYPE (const char *restrict filename, const Dwg_Object *restrict obj)
       MATCH_OBJECT (LTYPE, description, 3);
       if (!opt_tables)
         {
    -      MATCH_OBJECT (LTYPE, strings_area, 3);
    +      Dwg_Object_LTYPE *_obj = obj->tio.object->tio.LTYPE;
    +      for (BITCODE_BL i = 0; _obj && _obj->dashes && i < _obj->numdashes; i++)
    +        if (_obj->dashes[i].text)
    +          {
    +            if (obj->parent->header.version >= R_2007)
    +              {
    +                size_t max_wchars
    +                    = _obj->strings_area
    +                          ? (512
    +                             - ((BITCODE_RC *)_obj->dashes[i].text
    +                                - _obj->strings_area))
    +                                / 2
    +                          : 0;
    +                text = bit_convert_TU_len ((BITCODE_TU)_obj->dashes[i].text,
    +                                           max_wchars);
    +                if (text)
    +                  {
    +                    found += do_match (1, filename, "LTYPE", 9, text);
    +                    free (text);
    +                  }
    +              }
    +            else
    +              found
    +                  += do_match (0, filename, "LTYPE", 9, _obj->dashes[i].text);
    +          }
         }
       return found;
     }
    
  • src/bits.c+103 0 modified
    @@ -2919,6 +2919,109 @@ bit_convert_TU (const BITCODE_TU restrict wstr)
       return str;
     }
     
    +/* Bounded variant: returns NULL if not null-terminated within max_wchars. */
    +char *
    +bit_convert_TU_len (const BITCODE_TU restrict wstr, const size_t max_wchars)
    +{
    +  BITCODE_TU tmp = wstr;
    +  char *str;
    +  size_t wlen = 0;
    +  int i, len = 0;
    +  uint16_t c = 0;
    +
    +  if (!wstr || !max_wchars)
    +    return NULL;
    +  wlen = bit_wcs2nlen (wstr, max_wchars);
    +  if (wlen == 0 && wstr[0] != 0)
    +    return NULL; /* not null-terminated within max_wchars */
    +#ifdef HAVE_ALIGNED_ACCESS_REQUIRED
    +  if ((uintptr_t)wstr % SIZEOF_SIZE_T)
    +    {
    +      unsigned char *b = (unsigned char *)wstr;
    +      c = TU_to_int (b);
    +      while (c && wlen--)
    +        {
    +          len++;
    +          if (c >= 0x80)
    +            {
    +              len++;
    +              if (c >= 0x800)
    +                len++;
    +            }
    +          b += 2;
    +          c = TU_to_int (b);
    +        }
    +    }
    +  else
    +#endif
    +    {
    +      while ((c = *tmp++) && wlen--)
    +        {
    +          len++;
    +          if (c >= 0x80)
    +            {
    +              len++;
    +              if (c >= 0x800)
    +                len++;
    +            }
    +        }
    +    }
    +  str = (char *)malloc (len + 1);
    +  if (!str)
    +    {
    +      loglevel |= 1;
    +      LOG_ERROR ("Out of memory");
    +      return NULL;
    +    }
    +  i = 0;
    +  tmp = wstr;
    +#ifdef HAVE_ALIGNED_ACCESS_REQUIRED
    +  if ((uintptr_t)wstr % SIZEOF_SIZE_T)
    +    {
    +      unsigned char *b = (unsigned char *)wstr;
    +      c = TU_to_int (b);
    +      while (c && i < len)
    +        {
    +          if (c < 0x80)
    +            str[i++] = c & 0xFF;
    +          else if (c < 0x800)
    +            {
    +              str[i++] = (c >> 6) | 0xC0;
    +              str[i++] = (c & 0x3F) | 0x80;
    +            }
    +          else
    +            {
    +              str[i++] = (c >> 12) | 0xE0;
    +              str[i++] = ((c >> 6) & 0x3F) | 0x80;
    +              str[i++] = (c & 0x3F) | 0x80;
    +            }
    +          b += 2;
    +          c = TU_to_int (b);
    +        }
    +    }
    +  else
    +#endif
    +    while ((c = *tmp++) && i < len)
    +      {
    +        if (c < 0x80)
    +          str[i++] = c & 0xFF;
    +        else if (c < 0x800)
    +          {
    +            str[i++] = (c >> 6) | 0xC0;
    +            str[i++] = (c & 0x3F) | 0x80;
    +          }
    +        else
    +          {
    +            str[i++] = (c >> 12) | 0xE0;
    +            str[i++] = ((c >> 6) & 0x3F) | 0x80;
    +            str[i++] = (c & 0x3F) | 0x80;
    +          }
    +      }
    +  if (i <= len + 1)
    +    str[i] = '\0';
    +  return str;
    +}
    +
     #define EXTEND_SIZE(str, i, len)                                              \
       if (i > len)                                                                \
         {                                                                         \
    
  • src/bits.h+3 2 modified
    @@ -279,6 +279,8 @@ size_t bit_strnlen (const char *restrict str, const size_t maxlen);
     
     /* Convert UCS-2LE to UTF-8, returning a copy. */
     EXPORT char *bit_convert_TU (const BITCODE_TU restrict wstr) ATTRIBUTE_MALLOC;
    +EXPORT char *bit_convert_TU_len (const BITCODE_TU restrict wstr,
    +                                 const size_t max_wchars) ATTRIBUTE_MALLOC;
     EXPORT char *bit_TU_to_utf8_len (const BITCODE_TU restrict wstr,
                                      const int len) ATTRIBUTE_MALLOC;
     
    @@ -301,8 +303,7 @@ EXPORT
        iconv EINVAL fallback, empty src). Marking it malloc lets -O2 elide
        the `u8 != value` aliasing guard in VALUE_TV → free() of caller's
        stack buffer. */
    -char *bit_TV_to_utf8 (const char *restrict src,
    -                      const BITCODE_RS codepage);
    +char *bit_TV_to_utf8 (const char *restrict src, const BITCODE_RS codepage);
     
     /** Converts UTF-8 to UCS-2. Returns a copy.
         Needed by dwg importers, writers (e.g. dxf2dwg)
    

Vulnerability mechanics

Root cause

"Missing length-bounded scan in bit_convert_TU allows out-of-bounds heap read when LTYPE strings_area lacks a null terminator."

Attack vector

An attacker provides a malformed DWG file where an LTYPE object's `strings_area` (a 512-byte binary buffer) lacks a wide null terminator [patch_id=2539789]. When a local user runs `dwggrep` with a regex pattern against this file, `match_LTYPE` passes the unbounded buffer to `bit_convert_TU`, which reads past the 513-byte allocation (512 bytes + 1 byte padding) in 2-byte increments, causing a heap-buffer-overflow [ref_id=1]. The attack requires local access and the ability to supply a crafted DWG file to the `dwggrep` utility [ref_id=1].

Affected code

The vulnerable function is `bit_convert_TU` in `src/bits.c` (line 2830 in the unpatched version). The crash occurs when `dwggrep`'s `match_LTYPE` function in `programs/dwggrep.c` passes the raw `strings_area` buffer (a 512-byte heap allocation) directly to `bit_convert_TU`, which scans for a null terminator without any length bound [ref_id=1]. The patch introduces a bounded variant `bit_convert_TU_len` in `src/bits.c` and modifies `match_LTYPE` in `programs/dwggrep.c` to iterate over individual `dashes[i].text` entries instead of the whole buffer [patch_id=2539789].

What the fix does

The patch adds `bit_convert_TU_len()` in `src/bits.c`, a bounded variant that accepts a `max_wchars` parameter and returns NULL if no null terminator is found within that limit [patch_id=2539789]. In `programs/dwggrep.c`, `match_LTYPE` is rewritten to iterate over individual `dashes[i].text` strings rather than passing the entire `strings_area` buffer to `bit_convert_TU`. For R2007+ files, it computes `max_wchars` as the remaining space in the 512-byte `strings_area` and calls `bit_convert_TU_len` with that bound, preventing out-of-bounds reads [patch_id=2539789][ref_id=2].

Preconditions

  • inputAttacker must supply a malformed DWG file where an LTYPE object's strings_area lacks a wide null terminator
  • configVictim must run dwggrep with a regex pattern against the crafted DWG file
  • networkAttacker needs local access to the system to deliver the file

Reproduction

The public exploit is referenced in the issue report [ref_id=1]. The crash is reproduced by running `dwggrep ".*"` against a malformed DWG file (e.g., `id:000342,sig:06,src:005094,...`) that triggers a heap-buffer-overflow READ of size 2 in `bit_convert_TU` at `bits.c:2830` [ref_id=1]. The AddressSanitizer output confirms the overflow occurs at address `0x616000003580`, which is 0 bytes to the right of a 513-byte heap region allocated by `bit_read_TF` during LTYPE decoding [ref_id=1].

Generated on May 26, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.