Skip to content

Commit 6e7b82e

Browse files
bors[bot]brenoguim
andauthored
Merge #481
481: Do not let modifyRPath taint shared strings in strtab. Fix #315 r=Mic92 a=brenoguim Co-authored-by: Breno Rodrigues Guimaraes <[email protected]>
2 parents 336d634 + 860c04d commit 6e7b82e

File tree

5 files changed

+155
-13
lines changed

5 files changed

+155
-13
lines changed

src/patchelf.cc

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -664,14 +664,14 @@ template<ElfFileParams>
664664
void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff,
665665
Elf_Addr startAddr, Elf_Off startOffset)
666666
{
667-
/* Overwrite the old section contents with 'X's. Do this
667+
/* Overwrite the old section contents with 'Z's. Do this
668668
*before* writing the new section contents (below) to prevent
669669
clobbering previously written new section contents. */
670670
for (auto & i : replacedSections) {
671671
const std::string & sectionName = i.first;
672672
const Elf_Shdr & shdr = findSectionHeader(sectionName);
673673
if (rdi(shdr.sh_type) != SHT_NOBITS)
674-
memset(fileContents->data() + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size));
674+
memset(fileContents->data() + rdi(shdr.sh_offset), 'Z', rdi(shdr.sh_size));
675675
}
676676

677677
std::set<unsigned int> noted_phdrs = {};
@@ -1538,7 +1538,7 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
15381538

15391539
/* !!! We assume that the virtual address in the DT_STRTAB entry
15401540
of the dynamic section corresponds to the .dynstr section. */
1541-
auto shdrDynStr = findSectionHeader(".dynstr");
1541+
auto& shdrDynStr = findSectionHeader(".dynstr");
15421542
char * strTab = (char *) fileContents->data() + rdi(shdrDynStr.sh_offset);
15431543

15441544

@@ -1621,24 +1621,39 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
16211621
}
16221622
changed = true;
16231623

1624-
/* Zero out the previous rpath to prevent retained dependencies in
1625-
Nix. */
1624+
bool rpathStrShared = false;
16261625
size_t rpathSize = 0;
16271626
if (rpath) {
1628-
rpathSize = strlen(rpath);
1627+
std::string_view rpathView {rpath};
1628+
rpathSize = rpathView.size();
1629+
1630+
size_t rpathStrReferences = 0;
1631+
forAllStringReferences(shdrDynStr, [&] (auto refIdx) {
1632+
if (rpathView.end() == std::string_view(strTab + rdi(refIdx)).end())
1633+
rpathStrReferences++;
1634+
});
1635+
assert(rpathStrReferences >= 1);
1636+
debug("Number of rpath references: %lu\n", rpathStrReferences);
1637+
rpathStrShared = rpathStrReferences > 1;
1638+
}
1639+
1640+
/* Zero out the previous rpath to prevent retained dependencies in
1641+
Nix. */
1642+
if (rpath && !rpathStrShared) {
1643+
debug("Tainting old rpath with Xs\n");
16291644
memset(rpath, 'X', rpathSize);
16301645
}
16311646

16321647
debug("new rpath is '%s'\n", newRPath.c_str());
16331648

16341649

1635-
if (newRPath.size() <= rpathSize) {
1650+
if (!rpathStrShared && newRPath.size() <= rpathSize) {
16361651
if (rpath) memcpy(rpath, newRPath.c_str(), newRPath.size() + 1);
16371652
return;
16381653
}
16391654

16401655
/* Grow the .dynstr section to make room for the new RPATH. */
1641-
debug("rpath is too long, resizing...\n");
1656+
debug("rpath is too long or shared, resizing...\n");
16421657

16431658
std::string & newDynStr = replaceSection(".dynstr",
16441659
rdi(shdrDynStr.sh_size) + newRPath.size() + 1);
@@ -2293,6 +2308,42 @@ void ElfFile<ElfFileParamNames>::modifyExecstack(ExecstackMode op)
22932308
printf("execstack: %c\n", result);
22942309
}
22952310

2311+
template<ElfFileParams>
2312+
template<class StrIdxCallback>
2313+
void ElfFile<ElfFileParamNames>::forAllStringReferences(const Elf_Shdr& strTabHdr, StrIdxCallback&& fn)
2314+
{
2315+
for (auto& sym : tryGetSectionSpan<Elf_Sym>(".dynsym"))
2316+
fn(sym.st_name);
2317+
2318+
for (auto& dyn : tryGetSectionSpan<Elf_Dyn>(".dynamic"))
2319+
switch (rdi(dyn.d_tag))
2320+
{
2321+
case DT_NEEDED:
2322+
case DT_SONAME:
2323+
case DT_RPATH:
2324+
case DT_RUNPATH: fn(dyn.d_un.d_val);
2325+
default:;
2326+
}
2327+
2328+
if (auto verdHdr = tryFindSectionHeader(".gnu.version_d"))
2329+
{
2330+
if (&shdrs.at(rdi(verdHdr->get().sh_link)) == &strTabHdr)
2331+
forAll_ElfVer(getSectionSpan<Elf_Verdef>(*verdHdr),
2332+
[] (auto& /*vd*/) {},
2333+
[&] (auto& vda) { fn(vda.vda_name); }
2334+
);
2335+
}
2336+
2337+
if (auto vernHdr = tryFindSectionHeader(".gnu.version_r"))
2338+
{
2339+
if (&shdrs.at(rdi(vernHdr->get().sh_link)) == &strTabHdr)
2340+
forAll_ElfVer(getSectionSpan<Elf_Verneed>(*vernHdr),
2341+
[&] (auto& vn) { fn(vn.vn_file); },
2342+
[&] (auto& vna) { fn(vna.vna_name); }
2343+
);
2344+
}
2345+
}
2346+
22962347
static bool printInterpreter = false;
22972348
static bool printOsAbi = false;
22982349
static bool setOsAbi = false;
@@ -2397,9 +2448,9 @@ static void patchElf()
23972448
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
23982449

23992450
if (getElfType(fileContents).is32Bit)
2400-
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym, Elf32_Rel, Elf32_Rela, 32>(fileContents), fileContents, outputFileName2);
2451+
patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Versym, Elf32_Verdef, Elf32_Verdaux, Elf32_Verneed, Elf32_Vernaux, Elf32_Rel, Elf32_Rela, 32>(fileContents), fileContents, outputFileName2);
24012452
else
2402-
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym, Elf64_Rel, Elf64_Rela, 64>(fileContents), fileContents, outputFileName2);
2453+
patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Versym, Elf64_Verdef, Elf64_Verdaux, Elf64_Verneed, Elf64_Vernaux, Elf64_Rel, Elf64_Rela, 64>(fileContents), fileContents, outputFileName2);
24032454
}
24042455
}
24052456

src/patchelf.h

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
using FileContents = std::shared_ptr<std::vector<unsigned char>>;
1212

13-
#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Versym, class Elf_Rel, class Elf_Rela, unsigned ElfClass
14-
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym, Elf_Rel, Elf_Rela, ElfClass
13+
#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Versym, class Elf_Verdef, class Elf_Verdaux, class Elf_Verneed, class Elf_Vernaux, class Elf_Rel, class Elf_Rela, unsigned ElfClass
14+
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Versym, Elf_Verdef, Elf_Verdaux, Elf_Verneed, Elf_Vernaux, Elf_Rel, Elf_Rela, ElfClass
1515

1616
template<class T>
1717
struct span
@@ -237,6 +237,40 @@ class ElfFile
237237
}
238238
}
239239

240+
template<class StrIdxCallback>
241+
void forAllStringReferences(const Elf_Shdr& strTabHdr, StrIdxCallback&& fn);
242+
243+
template<class T, class U>
244+
auto follow(U* ptr, size_t offset) -> T* {
245+
return offset ? (T*)(((char*)ptr)+offset) : nullptr;
246+
};
247+
248+
template<class VdFn, class VaFn>
249+
void forAll_ElfVer(span<Elf_Verdef> vdspan, VdFn&& vdfn, VaFn&& vafn)
250+
{
251+
auto* vd = vdspan.begin();
252+
for (; vd; vd = follow<Elf_Verdef>(vd, rdi(vd->vd_next)))
253+
{
254+
vdfn(*vd);
255+
auto va = follow<Elf_Verdaux>(vd, rdi(vd->vd_aux));
256+
for (; va; va = follow<Elf_Verdaux>(va, rdi(va->vda_next)))
257+
vafn(*va);
258+
}
259+
}
260+
261+
template<class VnFn, class VaFn>
262+
void forAll_ElfVer(span<Elf_Verneed> vnspan, VnFn&& vnfn, VaFn&& vafn)
263+
{
264+
auto* vn = vnspan.begin();
265+
for (; vn; vn = follow<Elf_Verneed>(vn, rdi(vn->vn_next)))
266+
{
267+
vnfn(*vn);
268+
auto va = follow<Elf_Vernaux>(vn, rdi(vn->vn_aux));
269+
for (; va; va = follow<Elf_Vernaux>(va, rdi(va->vna_next)))
270+
vafn(*va);
271+
}
272+
}
273+
240274
/* Convert an integer in big or little endian representation (as
241275
specified by the ELF header) to this platform's integer
242276
representation. */

tests/Makefile.am

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ src_TESTS = \
4949
modify-execstack.sh \
5050
rename-dynamic-symbols.sh \
5151
overlapping-segments-after-rounding.sh \
52+
shared-rpath.sh \
5253
empty-note.sh
5354

5455
build_TESTS = \
@@ -121,7 +122,7 @@ check_DATA = libbig-dynstr.debug
121122
# - with libtool, it is difficult to control options
122123
# - with libtool, it is not possible to compile convenience *dynamic* libraries :-(
123124
check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libsimple-execstack.so libbuildid.so libtoomanystrtab.so \
124-
phdr-corruption.so many-syms-main libmany-syms.so liboveralign.so
125+
phdr-corruption.so many-syms-main libmany-syms.so liboveralign.so libshared-rpath.so
125126

126127
libbuildid_so_SOURCES = simple.c
127128
libbuildid_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,--build-id
@@ -148,6 +149,9 @@ libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,noexecstack
148149
liboveralign_so_SOURCES = simple.c
149150
liboveralign_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,max-page-size=0x10000
150151

152+
libshared_rpath_so_SOURCES = shared-rpath.c
153+
libshared_rpath_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-rpath=a_symbol_name
154+
151155
libsimple_execstack_so_SOURCES = simple.c
152156
libsimple_execstack_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,execstack
153157

tests/shared-rpath.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
int a_symbol_name;
2+
int foo() { return a_symbol_name; }

tests/shared-rpath.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#! /bin/sh -e
2+
3+
PATCHELF=$(readlink -f "../src/patchelf")
4+
SCRATCH="scratch/$(basename "$0" .sh)"
5+
READELF=${READELF:-readelf}
6+
7+
LIB_NAME="${PWD}/libshared-rpath.so"
8+
9+
rm -rf "${SCRATCH}"
10+
mkdir -p "${SCRATCH}"
11+
cd "${SCRATCH}"
12+
13+
has_x() {
14+
strings "$1" | grep -c "XXXXXXXX"
15+
}
16+
17+
nm -D "${LIB_NAME}" | grep a_symbol_name
18+
previous_cnt="$(strings "${LIB_NAME}" | grep -c a_symbol_name)"
19+
20+
echo "#### Number of a_symbol_name strings in the library: $previous_cnt"
21+
22+
echo "#### Rename the rpath to something larger than the original"
23+
# Pathelf should detect that the rpath string is shared with the symbol name string and avoid
24+
# tainting the string with Xs
25+
"${PATCHELF}" --set-rpath a_very_big_rpath_that_is_larger_than_original --output liblarge-rpath.so "${LIB_NAME}"
26+
27+
echo "#### Checking symbol is still there"
28+
nm -D liblarge-rpath.so | grep a_symbol_name
29+
30+
echo "#### Checking there are no Xs"
31+
[ "$(has_x liblarge-rpath.so)" -eq 0 ] || exit 1
32+
33+
current_cnt="$(strings liblarge-rpath.so | grep -c a_symbol_name)"
34+
echo "#### Number of a_symbol_name strings in the modified library: $current_cnt"
35+
[ "$current_cnt" -eq "$previous_cnt" ] || exit 1
36+
37+
echo "#### Rename the rpath to something shorter than the original"
38+
# Pathelf should detect that the rpath string is shared with the symbol name string and avoid
39+
# overwriting the existing string
40+
"${PATCHELF}" --set-rpath shrt_rpth --output libshort-rpath.so "${LIB_NAME}"
41+
42+
echo "#### Checking symbol is still there"
43+
nm -D libshort-rpath.so | grep a_symbol_name
44+
45+
echo "#### Number of a_symbol_name strings in the modified library: $current_cnt"
46+
current_cnt="$(strings libshort-rpath.so | grep -c a_symbol_name)"
47+
[ "$current_cnt" -eq "$previous_cnt" ] || exit 1
48+
49+
echo "#### Now liblarge-rpath.so should have its own rpath, so it should be allowed to taint it"
50+
"${PATCHELF}" --set-rpath a_very_big_rpath_that_is_larger_than_original__even_larger --output liblarge-rpath2.so liblarge-rpath.so
51+
[ "$(has_x liblarge-rpath2.so)" -eq 1 ] || exit 1

0 commit comments

Comments
 (0)