Add coverage for RHEL-45873

binutils: Avoid creating gaps between adjacent LOAD segments
This commit is contained in:
Miloš Prchlík 2024-08-08 19:19:18 +02:00
commit 7391c5eea6
5 changed files with 211 additions and 0 deletions

View file

@ -0,0 +1,9 @@
summary: Avoid creating gaps between adjacent LOAD segments
duration: 24h
require+:
- rpm-build
link+:
- verifies: https://issues.redhat.com/browse/RHEL-45873

View file

@ -0,0 +1,89 @@
#!/usr/bin/env sh
ITER=${1:-10000}
echo "Testing with $(rpm -q binutils)..."
pagesz=$(getconf PAGESIZE)
echo "Testing with a page size of $pagesz"
GLIBC_SCRIPTS="$(readlink -f "$(rpm --define="_topdir $TmpDir" --eval=%_builddir)/glibc-*/scripts")"
test() {
y=$(($1 * 7))
f=obj$y
gcc -O2 -fpie -pie -DN=$y t.c -Wl,-z,relro,-z,now -o $f
PYTHONPATH="$GLIBC_SCRIPTS:$PYTHONPATH" python tst-load-segment-gaps.py $f >> verbose.log
ret=$?
rm $f
return $ret
}
rm -f verbose.log
cat > t.c <<EOF
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <sys/prctl.h>
#include <unistd.h>
const char *const array[] = {
[N] = "value",
};
#define BUF_SZ 2048
void pmap() {
FILE *f;
size_t bytes;
char *buffer = malloc(BUF_SZ);
if (buffer == NULL)
err(EXIT_FAILURE, NULL);
f = fopen("/proc/self/maps", "r");
if (f == NULL)
err(EXIT_FAILURE, NULL);
while ((bytes = fread(buffer, 1, BUF_SZ, f)) > 0)
fwrite(buffer, 1, bytes, stdout);
if (ferror(f))
perror(NULL);
fclose(f);
free(buffer);
}
int main (void)
{
prctl(PR_SET_DUMPABLE, 0,0, 0);
char **volatile p = (char **)array;
pmap();
__builtin_puts(p[N]);
}
EOF
errors=0
# Print a blank line that we will rewrite later.
echo
for x in $(seq $ITER) ; do
p=$(($x * 100 / $ITER))
echo -e '\e[1A\e[K Progress:' "$p% Errors: $errors"
if ! test $x; then
errors=$(($errors + 1))
fi
done
rm t.c
if [[ "$errors" -eq 0 ]]; then
echo "The test succeeded!"
else
percent=$(($errors * 100 / $ITER))
echo "Error: tests had a $percent% failure rate" >&2
exit 1
fi

View file

@ -0,0 +1,28 @@
#!/bin/bash
# Include Beaker environment
. /usr/share/beakerlib/beakerlib.sh || exit 1
. "$TMT_TREE/tests/lib.sh" || exit 1
rlJournalStart
rlPhaseStartSetup
buTestHeader
buEnterTmpDir
buCopyReproducers "repro.sh tst-load-segment-gaps.py"
buPrepareSRPM "glibc"
buConfigureSRPM "glibc"
rlRun "tree $TmpDir"
rlPhaseEnd
rlPhaseStartTest
rlRun "./repro.sh"
rlPhaseEnd
rlPhaseStartCleanup
buExitTmpDir
rlPhaseEnd
rlJournalPrintText
rlJournalEnd

View file

@ -0,0 +1,74 @@
#!/usr/bin/python3
# Verify that objects do not contain gaps in load segments.
# Copyright (C) 2024 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
#
# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, see
# <https://www.gnu.org/licenses/>.
import argparse
import os.path
import sys
# Make available glibc Python modules.
sys.path.append(os.path.join(
os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'scripts'))
import glibcelf
def rounddown(val, align):
assert (align & (align - 1)) == 0, align
return val & -align
def roundup(val, align):
assert (align & (align - 1)) == 0, align
return (val + align - 1) & -align
errors = False
def process(path, img):
global errors
loads = [phdr for phdr in img.phdrs()
if phdr.p_type == glibcelf.Pt.PT_LOAD]
if not loads:
# Nothing ot check.
return
alignments = [phdr.p_align for phdr in loads if phdr.p_align > 0]
if alignments:
align = min(alignments)
else:
print('error: cannot infer page size')
errors = True
align = 4096
print('info: inferred page size:', align)
current_address = None
for idx, phdr in enumerate(loads):
this_address = rounddown(phdr.p_vaddr, align)
next_address = roundup(phdr.p_vaddr + phdr.p_memsz, align)
print('info: LOAD segment {}: address 0x{:x}, size {},'
' range [0x{:x},0x{:x})'
.format(idx, phdr.p_vaddr, phdr.p_memsz,
this_address, next_address))
if current_address is not None:
gap = this_address - current_address
if gap != 0:
errors = True
print('error: gap between load segments: {} bytes'.format(gap))
current_address = next_address
for path in sys.argv[1:]:
img = glibcelf.Image.readfile(path)
process(path, img)
if errors:
sys.exit(1)

View file

@ -116,6 +116,17 @@ function buPrepareSRPM () {
}
function buConfigureSRPM () {
if [ -n "$BU_AS_TEST_USER" ]; then
username="${1:-$BU_TEST_USER}"
rlRun "su - $username -c 'rpmbuild -bp --define=\"_topdir $TmpDir\" $SPECFILE'"
else
rlRun " rpmbuild -bp --define=\"_topdir $TmpDir\" $SPECFILE"
fi
}
function buBuildSRPM () {
if [ -n "$BU_AS_TEST_USER" ]; then
username="${1:-$BU_TEST_USER}"