git: d39e0bdc6b76 - stable/14 - rtld/arm: fix initial-exec (IE) thread-local storage relocation

From: R. Christian McDonald <rcm_at_FreeBSD.org>
Date: Fri, 22 Nov 2024 15:38:06 UTC
The branch stable/14 has been updated by rcm:

URL: https://cgit.FreeBSD.org/src/commit/?id=d39e0bdc6b765f9b2fca24fb67951b12ec47348e

commit d39e0bdc6b765f9b2fca24fb67951b12ec47348e
Author:     R. Christian McDonald <rcm@rcm.sh>
AuthorDate: 2023-11-03 12:56:58 +0000
Commit:     R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2024-11-22 15:35:06 +0000

    rtld/arm: fix initial-exec (IE) thread-local storage relocation
    
    net/frr[89] revealed an interesting edge-case on arm when dynamically
    linking a shared library that declares more than one static TLS variable
    with at least one  using the "initial-exec" TLS model. In the case
    of frr[89], this library was libfrr.so which essentially does the
    following:
    
            #include <stdio.h>
    
            #include "lib.h"
    
            static __thread int *a
                    __attribute__((tls_model("initial-exec")));
    
            void lib_test()
            {
                    static __thread int b = -1;
    
                    printf("&a = %p\n", &a);
                    printf(" a = %p\n", a);
    
                    printf("\n");
    
                    printf("&b = %p\n", &b);
                    printf(" b = %d\n", b);
            }
    
    Allocates a file scoped `static __thread` pointer with
    tls_model("initial-exec") and later a block scoped TLS int. Notice in
    the above minimal reproducer, `b == -1`. The relocation process does
    the wrong thing and ends up pointing both `a` and `b` at the same place
    in memory.
    
    The output of the above in the broken state is:
    
            &a = 0x4009c018
             a = 0xffffffff
    
            &b = 0x4009c018
             b = -1
    
    With the patch applied, the output becomes:
    
            &a = 0x4009c01c
             a = 0x0
    
            &b = 0x4009c018
             b = -1
    
    Reviewed by:    kib
    Approved by:    kp (mentor)
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D42415/
    
    (cherry picked from commit 98fd69f0090da73d9d0451bd769d7752468284c6)
---
 libexec/rtld-elf/arm/reloc.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c
index c3e95940be74..6efc9f499761 100644
--- a/libexec/rtld-elf/arm/reloc.c
+++ b/libexec/rtld-elf/arm/reloc.c
@@ -280,10 +280,13 @@ reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
 				return -1;
 
 			tmp = (Elf_Addr)def->st_value + defobj->tlsoffset;
-			if (__predict_true(RELOC_ALIGNED_P(where)))
+			if (__predict_true(RELOC_ALIGNED_P(where))) {
+				tmp += *where;
 				*where = tmp;
-			else
+			} else {
+				tmp += load_ptr(where);
 				store_ptr(where, tmp);
+			}
 			dbg("TLS_TPOFF32 %s in %s --> %p",
 			    obj->strtab + obj->symtab[symnum].st_name,
 			    obj->path, (void *)tmp);