From 67e30b15212adc1502b898a1ca224fdf65dc110d Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Thu, 29 Aug 2024 08:47:00 -0700 Subject: [PATCH] x86: Check invalid TLS descriptor call TLS descriptor call, call *x@tlsdesc(%rax) or call *x@tlsdesc(%eax) calls _dl_tlsdesc_return which expects that RAX/EAX points to the TLS descriptor. Update x86 linker to issue an error with or without TLS transition. bfd/ PR ld/32123 * elf32-i386.c (elf_i386_check_tls_transition): Move R_386_TLS_DESC_CALL to ... (elf_i386_tls_transition): Here. * elf64-x86-64.c (elf_x86_64_check_tls_transition): Move. R_X86_64_TLSDESC_CALL check to ... (elf_x86_64_tls_transition): Here. ld/ PR ld/32123 * testsuite/ld-i386/i386.exp: Run tlsgdesc3. * testsuite/ld-i386/tlsgdesc3.d: New file. * testsuite/ld-x86-64/tlsdesc5.d: Likewise. * testsuite/ld-x86-64/x86-64.exp: Run tlsdesc5. (cherry picked from commit:67e30b15212adc1502b898a1ca224fdf65dc110d) Upstream-Status: Submitted [https://sourceware.org/pipermail/binutils/2025-May/141321.html] CVE: CVE-2025-1179 Signed-off-by: Harish Sadineni --- bfd/elf32-i386.c | 44 +++++++++++++------ bfd/elf64-x86-64.c | 71 +++++++++++++++++++------------ ld/testsuite/ld-i386/i386.exp | 1 + ld/testsuite/ld-i386/tlsgdesc3.d | 5 +++ ld/testsuite/ld-x86-64/tlsdesc5.d | 5 +++ ld/testsuite/ld-x86-64/x86-64.exp | 1 + 6 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 ld/testsuite/ld-i386/tlsgdesc3.d create mode 100644 ld/testsuite/ld-x86-64/tlsdesc5.d diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index 18a28d2491c..9dea465f721 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -1039,19 +1039,8 @@ elf_i386_check_tls_transition (asection *sec, : elf_x86_tls_error_yes); case R_386_TLS_DESC_CALL: - /* Check transition from GDesc access model: - call *x@tlsdesc(%eax) - */ - if (offset + 2 <= sec->size) - { - /* Make sure that it's a call *x@tlsdesc(%eax). */ - call = contents + offset; - return (call[0] == 0xff && call[1] == 0x10 - ? elf_x86_tls_error_none - : elf_x86_tls_error_indirect_call); - } - - return elf_x86_tls_error_yes; + /* It has been checked in elf_i386_tls_transition. */ + return elf_x86_tls_error_none; default: abort (); @@ -1077,6 +1066,8 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd, unsigned int to_type = from_type; bool check = true; unsigned int to_le_type, to_ie_type; + bfd_vma offset; + bfd_byte *call; /* Skip TLS transition for functions. */ if (h != NULL @@ -1098,9 +1089,34 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd, switch (from_type) { + case R_386_TLS_DESC_CALL: + /* Check valid GDesc call: + call *x@tlsdesc(%eax) + */ + offset = rel->r_offset; + call = NULL; + if (offset + 2 <= sec->size) + { + /* Make sure that it's a call *x@tlsdesc(%eax). */ + call = contents + offset; + if (call[0] != 0xff || call[1] != 0x10) + call = NULL; + } + + if (call == NULL) + { + _bfd_x86_elf_link_report_tls_transition_error + (info, abfd, sec, symtab_hdr, h, sym, rel, + "R_386_TLS_DESC_CALL", NULL, + elf_x86_tls_error_indirect_call); + + return false; + } + + /* Fall through. */ + case R_386_TLS_GD: case R_386_TLS_GOTDESC: - case R_386_TLS_DESC_CALL: case R_386_TLS_IE_32: case R_386_TLS_IE: case R_386_TLS_GOTIE: diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index f116e423f61..7af2e607b02 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -1409,32 +1409,8 @@ elf_x86_64_check_tls_transition (bfd *abfd, : elf_x86_tls_error_yes); case R_X86_64_TLSDESC_CALL: - /* Check transition from GDesc access model: - call *x@tlsdesc(%rax) <--- LP64 mode. - call *x@tlsdesc(%eax) <--- X32 mode. - */ - if (offset + 2 <= sec->size) - { - unsigned int prefix; - call = contents + offset; - prefix = 0; - if (!ABI_64_P (abfd)) - { - /* Check for call *x@tlsdesc(%eax). */ - if (call[0] == 0x67) - { - prefix = 1; - if (offset + 3 > sec->size) - return elf_x86_tls_error_yes; - } - } - /* Make sure that it's a call *x@tlsdesc(%rax). */ - return (call[prefix] == 0xff && call[1 + prefix] == 0x10 - ? elf_x86_tls_error_none - : elf_x86_tls_error_indirect_call); - } - - return elf_x86_tls_error_yes; + /* It has been checked in elf_x86_64_tls_transition. */ + return elf_x86_tls_error_none; default: abort (); @@ -1459,6 +1435,8 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd, unsigned int from_type = *r_type; unsigned int to_type = from_type; bool check = true; + bfd_vma offset; + bfd_byte *call; /* Skip TLS transition for functions. */ if (h != NULL @@ -1468,10 +1446,49 @@ elf_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd, switch (from_type) { + case R_X86_64_TLSDESC_CALL: + /* Check valid GDesc call: + call *x@tlsdesc(%rax) <--- LP64 mode. + call *x@tlsdesc(%eax) <--- X32 mode. + */ + offset = rel->r_offset; + call = NULL; + if (offset + 2 <= sec->size) + { + unsigned int prefix; + call = contents + offset; + prefix = 0; + if (!ABI_64_P (abfd)) + { + /* Check for call *x@tlsdesc(%eax). */ + if (call[0] == 0x67) + { + prefix = 1; + if (offset + 3 > sec->size) + call = NULL; + } + } + + /* Make sure that it's a call *x@tlsdesc(%rax). */ + if (call != NULL + && (call[prefix] != 0xff || call[1 + prefix] != 0x10)) + call = NULL; + } + + if (call == NULL) + { + _bfd_x86_elf_link_report_tls_transition_error + (info, abfd, sec, symtab_hdr, h, sym, rel, + "R_X86_64_TLSDESC_CALL", NULL, + elf_x86_tls_error_indirect_call); + return false; + } + + /* Fall through. */ + case R_X86_64_TLSGD: case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_CODE_4_GOTPC32_TLSDESC: - case R_X86_64_TLSDESC_CALL: case R_X86_64_GOTTPOFF: case R_X86_64_CODE_4_GOTTPOFF: case R_X86_64_CODE_6_GOTTPOFF: diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp index a8db2c713f3..41e8725d059 100644 --- a/ld/testsuite/ld-i386/i386.exp +++ b/ld/testsuite/ld-i386/i386.exp @@ -543,6 +543,7 @@ run_dump_test "pr27998a" run_dump_test "pr27998b" run_dump_test "tlsgdesc1" run_dump_test "tlsgdesc2" +run_dump_test "tlsgdesc3" proc undefined_weak {cflags ldflags} { set testname "Undefined weak symbol" diff --git a/ld/testsuite/ld-i386/tlsgdesc3.d b/ld/testsuite/ld-i386/tlsgdesc3.d new file mode 100644 index 00000000000..f2c29d880f2 --- /dev/null +++ b/ld/testsuite/ld-i386/tlsgdesc3.d @@ -0,0 +1,5 @@ +#source: tlsgdesc2.s +#name: TLS GDesc call (indirect CALL) +#as: --32 +#ld: -shared -melf_i386 +#error: .*: relocation R_386_TLS_DESC_CALL against `foo' must be used in indirect CALL with EAX register only diff --git a/ld/testsuite/ld-x86-64/tlsdesc5.d b/ld/testsuite/ld-x86-64/tlsdesc5.d new file mode 100644 index 00000000000..6a0158b44b7 --- /dev/null +++ b/ld/testsuite/ld-x86-64/tlsdesc5.d @@ -0,0 +1,5 @@ +#source: tlsdesc4.s +#name: TLS GDesc call (indirect CALL) +#as: --64 +#ld: -shared -melf_x86_64 +#error: .*: relocation R_X86_64_TLSDESC_CALL against `foo' must be used in indirect CALL with RAX register only diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp index 811813466f8..82b0520c52a 100644 --- a/ld/testsuite/ld-x86-64/x86-64.exp +++ b/ld/testsuite/ld-x86-64/x86-64.exp @@ -744,6 +744,7 @@ run_dump_test "pr29820" run_dump_test "tlsie5" run_dump_test "tlsdesc3" run_dump_test "tlsdesc4" +run_dump_test "tlsdesc5" proc undefined_weak {cflags ldflags} { set testname "Undefined weak symbol" -- 2.49.0