|
| 1 | +#!/bin/sh |
| 2 | +# Prep script for x86_64 that recompiles glibc without vsyscalls. |
| 3 | +# |
| 4 | +# tl;dr: Because of |
| 5 | +# https://mail.python.org/pipermail/wheel-builders/2016-December/000239.html, |
| 6 | +# this removes vsyscall code from glibc. This requires building the |
| 7 | +# image on a system with vsyscall=emulate but allows the resulting |
| 8 | +# container to run on systems with vsyscall=none or vsyscall=emulate. |
| 9 | +# |
| 10 | +# "vsyscall" is an antiquated optimization for a small number of |
| 11 | +# frequently-used system calls. A vsyscall-enabeld Linux kernel would |
| 12 | +# map a read-only page of data and system calls into a process' memory |
| 13 | +# at a fixed address. These system calls could be invoked by |
| 14 | +# dereferencing a function pointers to fixed offsets in that page, |
| 15 | +# saving a relatively expensive context switch. [1] |
| 16 | +# |
| 17 | +# Unfortunately, because the code and its location in memory were |
| 18 | +# fixed and well-known, the vsyscall mechanism became a source of |
| 19 | +# gadgets for ROP attacks (specifically, Sigreturn-Oriented Programs) |
| 20 | +# [2]. Linux 3.1 introduced vsyscall emulation that prevented |
| 21 | +# attackers from jumping into the middle of the system calls' code at |
| 22 | +# the expense of speed, as well as the ability to disable it entirely. |
| 23 | +# [3][4] The vsyscall mechanism could not be eliminated at the time |
| 24 | +# because glibc versions earlier than 2.14 contained hard-coded |
| 25 | +# references to the fixed memory address, specifically in time(2). [5] |
| 26 | +# These segfault when attempting to issue a vsyscall against a kernel |
| 27 | +# that has disabled vsyscalls. |
| 28 | +# |
| 29 | +# Linux introduced a "virtual dynamic shared object" (vDSO) that |
| 30 | +# achieves the same high-speed, in-process system call mechanism via |
| 31 | +# shared objects sometime before the kernel's migration to git. While |
| 32 | +# old itself, vDSO's presentation as a shared library allows it to |
| 33 | +# benefit from ASLR on modern systems, making it no more amenable to |
| 34 | +# ROP gadgets than any other shared library. glibc only switched over |
| 35 | +# completely to vDSO as of glibc 2.25, so until recently vsyscall |
| 36 | +# emulation has remained on for most kernels. [6] Furthermore, i686 |
| 37 | +# does not use vsyscall at all, so it requires no patching. |
| 38 | +# |
| 39 | +# At the same time, vsyscall emulation still exposed values useful to |
| 40 | +# ROP attacks, so Linux 4.4 added a compilation option to disable |
| 41 | +# it. [7][8] Distributions are beginning to ship kernels configured |
| 42 | +# without vsyscall, and running CentOS 5 (glibc 2.5) or 6 (glibc 2.12) |
| 43 | +# Docker containers on these distributions indeed causes segfaults |
| 44 | +# without vsyscall=emulate [9][10]. CentOS 6, however, is supported |
| 45 | +# until 2020, and it's likely that more and more distributions will |
| 46 | +# ship with CONFIG_LEGACY_VSYSCALL_NONE. If managed CI services like |
| 47 | +# Travis make this switch, developers will be unable to build |
| 48 | +# manylinux2 wheels with our Docker image. |
| 49 | +# |
| 50 | +# Fortunately, vsyscall is merely an optimization, and patches that |
| 51 | +# remove it can be backported to glibc 2.12 and the library |
| 52 | +# recompiled. The result is a Docker image that can be run on kernels |
| 53 | +# regardless of their vsyscall configuration because executable and |
| 54 | +# libraries on CentOS are dynamically linked against glibc. Libraries |
| 55 | +# built on this image are unaffected because: |
| 56 | +# |
| 57 | +# a) the kernel only maps vsyscall pages into processes; |
| 58 | +# b) only glibc used the vsyscall interface directly, and it's |
| 59 | +# included in manylinux2's whitelist policy. |
| 60 | +# |
| 61 | +# Developers who build the manylinux2 Docker image itself, however, |
| 62 | +# must do so on a system with vsyscall=emulate. |
| 63 | +# |
| 64 | +# References: |
| 65 | +# [1] https://lwn.net/Articles/446528/ |
| 66 | +# [2] http://www.cs.vu.nl/~herbertb/papers/srop_sp14.pdf |
| 67 | +# [3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5cec93c216db77c45f7ce970d46283bcb1933884 |
| 68 | +# [4] https://www.kernel.org/pub/linux/kernel/v3.x/ChangeLog-3.1 |
| 69 | +# [5] https://sourceware.org/git/?p=glibc.git;a=blob;f=ChangeLog;h=3a6abda7d07fdaa367c48a9274cc1c08498964dc;hb=356f8bc660a154a07b03da7c536831da5c8f74fe |
| 70 | +# [6] https://sourceware.org/git/?p=glibc.git;a=blob;f=ChangeLog;h=6037fef737f0338a84c6fb564b3b8dc1b1221087;hb=58557c229319a3b8d2eefdb62e7df95089eabe37 |
| 71 | +# [7] https://googleprojectzero.blogspot.fr/2015/08/three-bypasses-and-fix-for-one-of.html |
| 72 | +# [8] https://outflux.net/blog/archives/2016/09/27/security-things-in-linux-v4-4/ |
| 73 | +# [9] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=852620#20 |
| 74 | +# [10] https://github.com/CentOS/sig-cloud-instance-images/issues/62 |
| 75 | + |
| 76 | +# Stop at any error, show all commands |
| 77 | +set -ex |
| 78 | + |
| 79 | +# Locate the prep directory |
| 80 | +MY_DIR=/$(dirname "${BASH_SOURCE[0]}") |
| 81 | + |
| 82 | +# glibc version |
| 83 | +GLIBC_VERSION=glibc-2.12-1.209 |
| 84 | + |
| 85 | +# Source RPM topdir |
| 86 | +SRPM_TOPDIR=/root/rpmbuild |
| 87 | + |
| 88 | +# Source RPM download directory |
| 89 | +DOWNLOADED_SRPMS=/root/srpms |
| 90 | + |
| 91 | +# Include the CentOS source RPM repository. |
| 92 | +# https://bugs.centos.org/view.php?id=1646 |
| 93 | +cp $MY_DIR/CentOS-source.repo /etc/yum.repos.d/CentOS-source.repo |
| 94 | + |
| 95 | +# Extract and prepare the source |
| 96 | +# https://blog.packagecloud.io/eng/2015/04/20/working-with-source-rpms/ |
| 97 | +yum -y update |
| 98 | +yum -y install yum-utils rpm-build |
| 99 | +yum-builddep -y glibc |
| 100 | +mkdir $DOWNLOADED_SRPMS |
| 101 | +# The glibc RPM's contents are owned by mockbuild |
| 102 | +adduser mockbuild |
| 103 | +# yumdownloader assumes the current working directory |
| 104 | +(cd $DOWNLOADED_SRPMS && yumdownloader --source glibc) |
| 105 | +rpm -ivh $DOWNLOADED_SRPMS/$GLIBC_VERSION.el6.src.rpm |
| 106 | +# Prepare the source by applying Red Hat and CentOS patches |
| 107 | +rpmbuild -bp $SRPM_TOPDIR/SPECS/glibc.spec |
| 108 | + |
| 109 | +# Copy the vsyscall removal patch into place |
| 110 | +cp $MY_DIR/remove-vsyscall.patch $SRPM_TOPDIR/SOURCES |
| 111 | +# Patch the RPM spec file so that it uses the vsyscall removal patch |
| 112 | +(cd $SRPM_TOPDIR/SPECS && patch -p2 < $MY_DIR/glibc.spec.patch) |
| 113 | + |
| 114 | +# Build the RPMS |
| 115 | +rpmbuild -ba $SRPM_TOPDIR/SPECS/glibc.spec |
| 116 | + |
| 117 | +# Install the replacement glibc |
| 118 | +rpm --install --force --nodeps $SRPM_TOPDIR/RPMS/x86_64/$GLIBC_VERSION.el6.x86_64.rpm |
| 119 | + |
| 120 | +# XXX: Remove all unneeded dependencies |
| 121 | +yum -y erase yum-utils rpm-build |
0 commit comments