libctf: bounds-check forward ctt_type before indexing pop[]

Message ID 178394860780.2.7020877874065223365@digiscrypt.com
State New
Headers
Series libctf: bounds-check forward ctt_type before indexing pop[] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed

Commit Message

Naveed Khan July 13, 2026, 1:16 p.m. UTC
  init_static_types_internal() walks the CTF type section that comes
directly from an object file's .ctf section.  During the first counting
pass it treats a CTF_K_FORWARD record's ctt_type as the CTF_K_* kind of
the forwarded tag and bumps the corresponding population count:

	if (kind == CTF_K_FORWARD)
	  pop[tp->ctt_type]++;

pop[] is a fixed-size stack array with CTF_K_MAX + 1 (64) entries, but
ctt_type is a 32-bit value read straight from the file and is never
validated.  A crafted dict whose forward record carries a ctt_type
greater than CTF_K_MAX therefore causes an out-of-bounds write to the
stack at an attacker-controlled index.

The type walk is reachable from ctf_bufopen()/ctf_open(), i.e. whenever
libctf opens a dict: ld while linking CTF, and objdump/nm --ctf.  The
second pass already tolerates an out-of-range ctt_type (ctf_name_table()
has a default case), so only this first-pass index was unguarded.

Reject a ctt_type outside the valid kind range as ECTF_CORRUPT, matching
the existing corruption handling in the same loop.

Reproduced with a 65-byte in-memory dict (one CTF_K_FORWARD record whose
ctt_type is 64) passed to ctf_bufopen().  Before the fix, AddressSanitizer
reports a stack-buffer-overflow at ctf-open.c:759 overflowing pop[64];
after the fix ctf_bufopen() returns ECTF_CORRUPT.  Valid forwards
(ctt_type of CTF_K_STRUCT/UNION/ENUM) and the boundary value CTF_K_MAX
still open successfully.

Signed-off-by: Naveed Khan <naveed@digiscrypt.com>
---
  

Comments

Alan Modra July 16, 2026, 12:20 a.m. UTC | #1
On Mon, Jul 13, 2026 at 06:46:47PM +0530, Naveed Khan wrote:
> diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c
> index 584d502a..73ff1ab7 100644
> --- a/libctf/ctf-open.c
> +++ b/libctf/ctf-open.c
> @@ -754,9 +754,15 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth,
>  	return ECTF_CORRUPT;
>  
>        /* For forward declarations, ctt_type is the CTF_K_* kind for the tag,
> -	 so bump that population count too.  */
> +	 so bump that population count too.  A corrupt dict may store an
> +	 out-of-range kind here, so guard against indexing pop[] out of
> +	 bounds.  */
>        if (kind == CTF_K_FORWARD)
> -	pop[tp->ctt_type]++;
> +	{
> +	  if (tp->ctt_type > CTF_K_MAX)
> +	    return ECTF_CORRUPT;
> +	  pop[tp->ctt_type]++;
> +	}
>  
>        tp = (ctf_type_t *) ((uintptr_t) tp + increment + vbytes);
>        pop[kind]++;
> -- 
> 2.52.0

Thanks, pushed.
  

Patch

diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c
index 584d502a..73ff1ab7 100644
--- a/libctf/ctf-open.c
+++ b/libctf/ctf-open.c
@@ -754,9 +754,15 @@  init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth,
 	return ECTF_CORRUPT;
 
       /* For forward declarations, ctt_type is the CTF_K_* kind for the tag,
-	 so bump that population count too.  */
+	 so bump that population count too.  A corrupt dict may store an
+	 out-of-range kind here, so guard against indexing pop[] out of
+	 bounds.  */
       if (kind == CTF_K_FORWARD)
-	pop[tp->ctt_type]++;
+	{
+	  if (tp->ctt_type > CTF_K_MAX)
+	    return ECTF_CORRUPT;
+	  pop[tp->ctt_type]++;
+	}
 
       tp = (ctf_type_t *) ((uintptr_t) tp + increment + vbytes);
       pop[kind]++;