Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96be7205 authored by Hristo Bojinov's avatar Hristo Bojinov
Browse files

Working ASLR implementation.



ASLR for shared libraries is controlled by "-a" in ota_from_target_files.
Binary files are self-contained (supported by apriori/soslim).

Signed-off-by: default avatarHristo Bojinov <hristo@google.com>
Change-Id: I500e325bf4a70a8d69a2ab9b2938e83dadb4e65d
parent 778c2b69
Loading
Loading
Loading
Loading
+189 −15
Original line number Diff line number Diff line
#include <stdio.h>
#include <stdlib.h>
#include <common.h>
#include <debug.h>
#include <libelf.h>
@@ -54,6 +55,154 @@ extern int verbose_flag;

static source_t *sources = NULL;

/* Retouch data is a very concise representation of the resolved relocations.
   This data is used to randomize the location of prelinked libraries at
   update time, on the device.
 */

// We will store retouch entries into this buffer, then dump them at the
// end of the .so file before setup_prelink_info().
#define RETOUCH_MAX_SIZE 500000
static char *retouch_buf;
static unsigned int retouch_byte_cnt;
// Compression state.
static int32_t offs_prev;
static uint32_t cont_prev;

#define false 0
#define true 1

void retouch_init(void) {
    offs_prev = 0;
    cont_prev = 0;
    retouch_byte_cnt = 0;
    retouch_buf = malloc(RETOUCH_MAX_SIZE+12);
    FAILIF(retouch_buf == NULL,
           "Could not allocate %d bytes.\n", RETOUCH_MAX_SIZE+12);
}

//
// We use three encoding schemes; this takes care of much of the redundancy
// inherent in lists of relocations:
//
//   * two bytes, leading 1, 2b for d_offset ("s"), 13b for d_contents ("c")
//
//     76543210 76543210
//     1ssccccc cccccccc
//
//   * three bytes, leading 01, 2b for delta offset, 20b for delta contents
//
//     76543210 76543210 76543210
//     01sscccc cccccccc cccccccc
//
//   * eigth bytes, leading 00, 30b for offset, 32b for contents
//
//     76543210 76543210 76543210 76543210
//     00ssssss ssssssss ssssssss ssssssss + 4 bytes contents
//
// NOTE 1: All deltas are w.r.t. the previous line in the list.
// NOTE 2: Two-bit ("ss") offsets mean: "00"=4, "01"=8, "10"=12, and "11"=16.
// NOTE 3: Delta contents are signed. To map back to a int32 we refill with 1s.
// NOTE 4: Special encoding for -1 offset. Extended back to 32b when decoded.
//

void retouch_encode(int32_t offset, uint32_t contents) {
    int64_t d_offs = offset-offs_prev;
    int64_t d_cont = (int64_t)contents-(int64_t)cont_prev;

    uint8_t output[8];
    uint32_t output_size;

    if ((d_offs > 3) &&
        (d_offs % 4) == 0 &&
        (d_offs / 4) < 5 &&
        (d_cont < 4000) &&
        (d_cont > -4000)) {
        // we can fit in 2 bytes
        output[0] =
          0x80 |
          (((d_offs/4)-1) << 5) |
          (((uint64_t)d_cont & 0x1f00) >> 8);
        output[1] =
          ((uint64_t)d_cont & 0xff);
        output_size = 2;
    } else if ((d_offs > 3) &&
               (d_offs % 4) == 0 &&
               (d_offs / 4) < 5 &&
               (d_cont < 510000) &&
               (d_cont > -510000)) {
        // fit in 3 bytes
        output[0] =
          0x40 |
          (((d_offs/4)-1) << 4) |
          (((uint64_t)d_cont & 0xf0000) >> 16);
        output[1] =
          ((uint64_t)d_cont & 0xff00) >> 8;
        output[2] =
          ((uint64_t)d_cont & 0xff);
        output_size = 3;
    } else {
        // fit in 8 bytes; we can't support files bigger than (1GB-1)
        // with this encoding: no library is that big anyway..
        FAILIF(offset < -1 || offset > 0x3ffffffe, "Offset out of range.\n");
        output[0] = (offset & 0x3f000000) >> 24;
        output[1] = (offset & 0xff0000) >> 16;
        output[2] = (offset & 0xff00) >> 8;
        output[3] = (offset & 0xff);
        output[4] = (contents & 0xff000000) >> 24;
        output[5] = (contents & 0xff0000) >> 16;
        output[6] = (contents & 0xff00) >> 8;
        output[7] = (contents & 0xff);
        output_size = 8;
    }

    // If this happens, the retouch buffer size can be bumped up.
    // Currently, the largest case is libwebcore, at about 250K.
    FAILIF((retouch_byte_cnt+output_size) > RETOUCH_MAX_SIZE,
           "About to overflow retouch buffer.\n");

    memcpy(retouch_buf+retouch_byte_cnt, output, output_size);
    retouch_byte_cnt += output_size;

    offs_prev = offset;
    cont_prev = contents;
}

void retouch_dump(const char *fname, int elf_little,
                  unsigned int retouch_byte_cnt, char *retouch_buf) {
    int fd = open(fname, O_WRONLY);
    FAILIF(fd < 0,
           "open(%s, O_WRONLY): %s (%d)\n" ,
           fname, strerror(errno), errno);
    off_t sz = lseek(fd, 0, SEEK_END);
    FAILIF(sz == (off_t)-1,
           "lseek(%d, 0, SEEK_END): %s (%d)!\n",
           fd, strerror(errno), errno);

    // The retouch blob ends with "RETOUCH XXXX", where XXXX is the 4-byte
    // size of the retouch blob, in target endianness.
    strncpy(retouch_buf+retouch_byte_cnt, "RETOUCH ", 8);
    if (elf_little ^ is_host_little()) {
        *(unsigned int *)(retouch_buf+retouch_byte_cnt+8) =
          switch_endianness(retouch_byte_cnt);
    } else {
        *(unsigned int *)(retouch_buf+retouch_byte_cnt+8) =
          retouch_byte_cnt;
    }

    int num_written = write(fd, retouch_buf, retouch_byte_cnt+12);
    FAILIF(num_written < 0,
           "write(%d, &info, sizeof(info)): %s (%d)\n",
           fd, strerror(errno), errno);
    FAILIF((retouch_byte_cnt+12) != num_written,
           "Could not write %d bytes as expected (wrote %d bytes instead)!\n",
           retouch_byte_cnt, num_written);
    FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
}

/* End of retouch code.
 */

#if defined(DEBUG) && 0

static void print_shdr(source_t *source, Elf_Scn *scn)
@@ -325,6 +474,9 @@ static Elf * init_elf(source_t *source, bool create_new_sections)
                   basename(source->name));
        }

        /* Save some of the info; needed for retouching (ASLR). */
        retouch_init();

        source->newelf_fd = open(source->output,
                                 O_RDWR | O_CREAT,
                                 0666);
@@ -775,6 +927,11 @@ static void destroy_source(source_t *source)
               function setup_prelink_info() below. */
            INFO("%s: setting up prelink tag at end of file.\n",
                 source->output ? source->output : source->name);
            retouch_encode(-1, source->base);
            retouch_dump(source->output ? source->output : source->name,
                         source->elf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
                         retouch_byte_cnt,
                         retouch_buf);
            setup_prelink_info(source->output ? source->output : source->name,
                               source->elf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
                               source->base);
@@ -785,6 +942,7 @@ static void destroy_source(source_t *source)
#endif/*SUPPORT_ANDROID_PRELINK_TAGS*/

    do_destroy_source(source);
    if (retouch_buf != NULL) { free(retouch_buf); retouch_buf = NULL; }

    if (source->shstrtab_data != NULL)
        FREEIF(source->shstrtab_data->d_buf); /* adjust_elf */
@@ -1226,9 +1384,12 @@ static int do_prelink(source_t *source,
                       rel->r_offset,
                       found_sym->st_value,
                       sym_source->base);
                  if (!dry_run)
                  if (!dry_run) {
                    PRINT("WARNING: Relocation type not supported "
                          "for retouching!");
                    *dest = found_sym->st_value + sym_source->base;
                  }
                }
              num_relocations++;
              break;
            case R_ARM_RELATIVE:
@@ -1240,8 +1401,15 @@ static int do_prelink(source_t *source,
                   sname,
                   symname ?: "(symbol has no name)",
                   rel->r_offset, *dest, source->base);
              if (!dry_run)
              if (!dry_run) {
                *dest += source->base;

                /* Output an entry for the ASLR touch-up process. */
                retouch_encode(rel->r_offset
                               -shdr_mem.sh_addr
                               +shdr_mem.sh_offset,
                               *dest);
              }
              num_relocations++;
              break;
            case R_ARM_COPY:
@@ -1352,18 +1520,24 @@ static int do_prelink(source_t *source,
                            ASSERT(data->d_buf != NULL);
                            ASSERT(data->d_size >= rel->r_offset -
                                   shdr_mem.sh_addr);
                            if (!dry_run)
                            if (!dry_run) {
                                PRINT("WARNING: Relocation type not supported "
                                      "for retouching!");
                                memcpy(dest, src, found_sym->st_size);
                            }
                          }
                        else {
                          ASSERT(src == NULL);
                          ASSERT(elf_ndxscn(src_scn) ==
                                 elf_ndxscn(sym_source->bss.scn));
                          if (!dry_run)
                          if (!dry_run) {
                              PRINT("WARNING: Relocation type not supported "
                                    "for retouching!");
                              memset(dest, 0, found_sym->st_size);
                          }
                        }
                      }
                    }
                  num_relocations++;
                }
              break;
+2 −1
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ struct source_t {
    Elf_Data *shstrtab_data;
    int elf_fd;
    int newelf_fd; /* fd of output file, -1 if output == NULL */
    int newelf_relo_fd; /* fd of relocaion output file */
    struct stat elf_file_info;
    GElf_Ehdr elf_hdr, oldelf_hdr;
    size_t shstrndx;
+14 −0
Original line number Diff line number Diff line
@@ -235,6 +235,20 @@ class EdifyGenerator(object):
             ",\0".join(['"' + i + '"' for i in sorted(links)]) + ");")
      self.script.append(self._WordWrap(cmd))

  def RetouchBinaries(self, file_list):
    """Execute the retouch instructions in files listed."""
    cmd = ('retouch_binaries(' +
           ', '.join(['"' + i[0] + '", "' + i[1] + '"' for i in file_list]) +
           ');')
    self.script.append(self._WordWrap(cmd))

  def UndoRetouchBinaries(self, file_list):
    """Undo the retouching (retouch to zero offset)."""
    cmd = ('undo_retouch_binaries(' +
           ', '.join(['"' + i[0] + '", "' + i[1] + '"' for i in file_list]) +
           ');')
    self.script.append(self._WordWrap(cmd))

  def AppendExtra(self, extra):
    """Append text verbatim to the output script."""
    self.script.append(extra)
+42 −14
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
  -e  (--extra_script)  <file>
      Insert the contents of file at the end of the update script.

  -a  (--use_aslr)
      Specify whether to build the package with ASLR enabled (off by default).
"""

import sys
@@ -75,6 +77,7 @@ OPTIONS.patch_threshold = 0.95
OPTIONS.wipe_user_data = False
OPTIONS.omit_prereq = False
OPTIONS.extra_script = None
OPTIONS.aslr_mode = False
OPTIONS.worker_threads = 3

def MostPopularKey(d, default):
@@ -91,6 +94,10 @@ def IsSymlink(info):
  symlink."""
  return (info.external_attr >> 16) == 0120777

def IsRegular(info):
  """Return true if the zipfile.ZipInfo object passed in represents a
  symlink."""
  return (info.external_attr >> 28) == 010


class Item:
@@ -246,13 +253,15 @@ def CopySystemFiles(input_zip, output_zip=None,
                    substitute=None):
  """Copies files underneath system/ in the input zip to the output
  zip.  Populates the Item class with their metadata, and returns a
  list of symlinks.  output_zip may be None, in which case the copy is
  skipped (but the other side effects still happen).  substitute is an
  optional dict of {output filename: contents} to be output instead of
  certain input files.
  list of symlinks as well as a list of files that will be retouched.
  output_zip may be None, in which case the copy is skipped (but the
  other side effects still happen).  substitute is an optional dict
  of {output filename: contents} to be output instead of certain input
  files.
  """

  symlinks = []
  retouch_files = []

  for info in input_zip.infolist():
    if info.filename.startswith("SYSTEM/"):
@@ -270,6 +279,9 @@ def CopySystemFiles(input_zip, output_zip=None,
            data = substitute[fn]
          else:
            data = input_zip.read(info.filename)
          if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
            retouch_files.append(("/system/" + basefilename,
                                  sha.sha(data).hexdigest()))
          output_zip.writestr(info2, data)
        if fn.endswith("/"):
          Item.Get(fn[:-1], dir=True)
@@ -277,7 +289,7 @@ def CopySystemFiles(input_zip, output_zip=None,
          Item.Get(fn, dir=False)

  symlinks.sort()
  return symlinks
  return (symlinks, retouch_files)


def SignOutput(temp_zip_name, output_zip_name):
@@ -375,8 +387,12 @@ def WriteFullOTAPackage(input_zip, output_zip, info):
  script.UnpackPackageDir("recovery", "/system")
  script.UnpackPackageDir("system", "/system")

  symlinks = CopySystemFiles(input_zip, output_zip)
  (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
  script.MakeSymlinks(symlinks)
  if OPTIONS.aslr_mode:
    script.RetouchBinaries(retouch_files)
  else:
    script.UndoRetouchBinaries(retouch_files)

  boot_img = File("boot.img", common.BuildBootableImage(
      os.path.join(OPTIONS.input_tmp, "BOOT")))
@@ -432,12 +448,17 @@ def LoadSystemFiles(z):
  """Load all the files from SYSTEM/... in a given target-files
  ZipFile, and return a dict of {filename: File object}."""
  out = {}
  retouch_files = []
  for info in z.infolist():
    if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
      fn = "system/" + info.filename[7:]
      basefilename = info.filename[7:]
      fn = "system/" + basefilename
      data = z.read(info.filename)
      out[fn] = File(fn, data)
  return out
      if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
        retouch_files.append(("/system/" + basefilename,
                              out[fn].sha1))
  return (out, retouch_files)


DIFF_PROGRAM_BY_EXT = {
@@ -600,9 +621,9 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip, info):
      metadata=metadata)

  print "Loading target..."
  target_data = LoadSystemFiles(target_zip)
  (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
  print "Loading source..."
  source_data = LoadSystemFiles(source_zip)
  (source_data, source_retouch_files) = LoadSystemFiles(source_zip)

  verbatim_targets = []
  patch_list = []
@@ -769,7 +790,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip, info):

  script.ShowProgress(0.1, 10)

  target_symlinks = CopySystemFiles(target_zip, None)
  (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)

  target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
  temp_script = script.MakeTemporary()
@@ -778,7 +799,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip, info):

  # Note that this call will mess up the tree of Items, so make sure
  # we're done with it.
  source_symlinks = CopySystemFiles(source_zip, None)
  (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
  source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])

  # Delete all the symlinks in source that aren't in target.  This
@@ -812,6 +833,10 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip, info):
      to_create.append((dest, link))
  script.DeleteFiles([i[1] for i in to_create])
  script.MakeSymlinks(to_create)
  if OPTIONS.aslr_mode:
    script.RetouchBinaries(target_retouch_files)
  else:
    script.UndoRetouchBinaries(target_retouch_files)

  # Now that the symlinks are created, we can set all the
  # permissions.
@@ -842,6 +867,8 @@ def main(argv):
      OPTIONS.omit_prereq = True
    elif o in ("-e", "--extra_script"):
      OPTIONS.extra_script = a
    elif o in ("-a", "--use_aslr"):
      OPTIONS.aslr_mode = True
    elif o in ("--worker_threads"):
      OPTIONS.worker_threads = int(a)
    else:
@@ -849,14 +876,15 @@ def main(argv):
    return True

  args = common.ParseOptions(argv, __doc__,
                             extra_opts="b:k:i:d:wne:",
                             extra_opts="b:k:i:d:wne:a",
                             extra_long_opts=["board_config=",
                                              "package_key=",
                                              "incremental_from=",
                                              "wipe_user_data",
                                              "no_prereq",
                                              "extra_script=",
                                              "worker_threads="],
                                              "worker_threads=",
                                              "use_aslr"],
                             extra_option_handler=option_handler)

  if len(args) != 2:
+16 −2
Original line number Diff line number Diff line
@@ -188,9 +188,13 @@ int main(int argc, char **argv)
            else INFO("Not building symbol filter, filter file is empty.\n");
        }
#ifdef SUPPORT_ANDROID_PRELINK_TAGS
        int prelinked = 0;
        int prelinked = 0, retouched = 0;
        int elf_little; /* valid if prelinked != 0 */
        long prelink_addr; /* valid if prelinked != 0 */
#define RETOUCH_MAX_SIZE 500000
        /* _cnt valid if retouched != 0 */
        unsigned int retouch_byte_cnt = RETOUCH_MAX_SIZE;
        char retouch_buf[RETOUCH_MAX_SIZE]; /* valid if retouched != 0 */
#endif
        clone_elf(elf, newelf,
                  infile, outfile,
@@ -200,7 +204,10 @@ int main(int argc, char **argv)
#ifdef SUPPORT_ANDROID_PRELINK_TAGS
                  , &prelinked,
                  &elf_little,
                  &prelink_addr
                  &prelink_addr,
                  &retouched,
                  &retouch_byte_cnt,
                  retouch_buf
#endif
                  ,
                  true, /* rebuild the section-header-strings table */
@@ -223,6 +230,13 @@ int main(int argc, char **argv)
               infile, strerror(errno), errno);

#ifdef SUPPORT_ANDROID_PRELINK_TAGS
        if (retouched) {
            INFO("File has retouch data, putting it back in place.\n");
            retouch_dump(outfile != NULL ? outfile : infile,
                         elf_little,
                         retouch_byte_cnt,
                         retouch_buf);
        }
        if (prelinked) {
            INFO("File is prelinked, putting prelink TAG back in place.\n");
            setup_prelink_info(outfile != NULL ? outfile : infile,
Loading