Blame SOURCES/rhbz1643997.0026-PR23860-additional-ugly-stack-clobber-protection-for.patch

e4e640
From a7605f298f8d823429b8cfd264bd28c3bd345eb5 Mon Sep 17 00:00:00 2001
e4e640
From: Serhei Makarov <smakarov@redhat.com>
e4e640
Date: Fri, 9 Nov 2018 14:36:19 -0500
e4e640
Subject: [PATCH 26/32] PR23860: additional ugly stack/clobber protection for
e4e640
 strings
e4e640
e4e640
In addition to prior commit, emit_string_copy() does not work for
e4e640
overlapping source/destination. So, make sure strings are not
e4e640
allocated in a way which overlaps map key/value arguments.
e4e640
e4e640
This increases space pressure, inducing a couple of bpf-asm.exp
e4e640
testcase failures.
e4e640
e4e640
* bpf-internal.h (value::format_str): New flag.
e4e640
(value::value): Take format_str flag.
e4e640
(value::mk_str): Take format_str flag.
e4e640
(program::format_map): New field, caches format_str separately.
e4e640
(program::new_str): Take format_str flag.
e4e640
* bpf-base.cxx (program::new_str): Cache format_str separately.
e4e640
* bpf-opt.cxx (alloc_literal_str): Store non-format str in lower half.
e4e640
* bpf-translate.cxx (emit_string_copy): Comment -- doesn't support overlap.
e4e640
(emit_string_copy): DEBUG_CODEGEN -- identify if zero-padding was done.
e4e640
(emit_print_format): Set format_str flag.
e4e640
---
e4e640
 bpf-base.cxx      | 13 ++++++++-----
e4e640
 bpf-internal.h    | 15 +++++++++++----
e4e640
 bpf-opt.cxx       | 35 ++++++++++++++++++++++++-----------
e4e640
 bpf-translate.cxx |  7 +++++--
e4e640
 4 files changed, 48 insertions(+), 22 deletions(-)
e4e640
e4e640
diff --git a/bpf-base.cxx b/bpf-base.cxx
e4e640
index 5d132bcd1..b19b69133 100644
e4e640
--- a/bpf-base.cxx
e4e640
+++ b/bpf-base.cxx
e4e640
@@ -657,14 +657,17 @@ program::new_imm(int64_t i)
e4e640
 }
e4e640
 
e4e640
 value *
e4e640
-program::new_str(std::string str)
e4e640
+program::new_str(std::string str, bool format_str)
e4e640
 {
e4e640
-  auto old = str_map.find(str);
e4e640
-  if (old != str_map.end())
e4e640
+  std::unordered_map<std::string, value *>& m = str_map;
e4e640
+  if (format_str) m = format_map;
e4e640
+
e4e640
+  auto old = m.find(str);
e4e640
+  if (old != m.end())
e4e640
     return old->second;
e4e640
 
e4e640
-  value *v = new value(value::mk_str(str));
e4e640
-  auto ok = str_map.insert(std::pair<std::string, value *>(str, v));
e4e640
+  value *v = new value(value::mk_str(str, format_str));
e4e640
+  auto ok = m.insert(std::pair<std::string, value *>(str, v));
e4e640
   assert(ok.second);
e4e640
   return v;
e4e640
 }
e4e640
diff --git a/bpf-internal.h b/bpf-internal.h
e4e640
index 75fefb769..f97501d7d 100644
e4e640
--- a/bpf-internal.h
e4e640
+++ b/bpf-internal.h
e4e640
@@ -78,18 +78,24 @@ struct value
e4e640
   int64_t imm_val;
e4e640
   std::string str_val;
e4e640
 
e4e640
-  value(value_type t = UNINIT, regno r = noreg, int64_t c = 0, std::string s = "")
e4e640
-    : type(t), reg_val(r), imm_val(c), str_val(s)
e4e640
+  bool format_str; // for str_val
e4e640
+
e4e640
+  value(value_type t = UNINIT, regno r = noreg, int64_t c = 0,
e4e640
+        std::string s = "", bool format_str = false)
e4e640
+  : type(t), reg_val(r), imm_val(c), str_val(s), format_str(format_str)
e4e640
   { }
e4e640
 
e4e640
   static value mk_imm(int64_t i) { return value(IMM, noreg, i); }
e4e640
-  static value mk_str(std::string s) { return value(STR, noreg, 0, s); }
e4e640
+  static value mk_str(std::string s, bool format_str = false) {
e4e640
+    return value(STR, noreg, 0, s, format_str);
e4e640
+  }
e4e640
   static value mk_reg(regno r) { return value(TMPREG, r); }
e4e640
   static value mk_hardreg(regno r) { return value(HARDREG, r); }
e4e640
 
e4e640
   bool is_reg() const { return type >= HARDREG; }
e4e640
   bool is_imm() const { return type == IMM; }
e4e640
   bool is_str() const { return type == STR; }
e4e640
+  bool is_format() const { assert(is_str()); return format_str; }
e4e640
 
e4e640
   regno reg() const { assert(is_reg()); return reg_val; }
e4e640
   int64_t imm() const { assert(is_imm()); return imm_val; }
e4e640
@@ -262,12 +268,13 @@ struct program
e4e640
   // Store at most one of each IMM and STR value:
e4e640
   std::unordered_map<int64_t, value *> imm_map;
e4e640
   std::unordered_map<std::string, value *> str_map;
e4e640
+  std::unordered_map<std::string, value *> format_map;
e4e640
 
e4e640
   regno max_reg() const { return reg_vals.size() + MAX_BPF_REG; }
e4e640
   value *lookup_reg(regno r);
e4e640
   value *new_reg();
e4e640
   value *new_imm(int64_t);
e4e640
-  value *new_str(std::string);
e4e640
+  value *new_str(std::string, bool format_str = false);
e4e640
 
e4e640
   // The BPF local stack is [0, -512] indexed off BPF_REG_10.
e4e640
   // The translator has dibs on the low bytes, [0, -max_tmp_space],
e4e640
diff --git a/bpf-opt.cxx b/bpf-opt.cxx
e4e640
index 089dcde55..f3aa5c462 100644
e4e640
--- a/bpf-opt.cxx
e4e640
+++ b/bpf-opt.cxx
e4e640
@@ -19,38 +19,51 @@ namespace bpf {
e4e640
 
e4e640
 // Allocate space on the stack and store a string literal in that space:
e4e640
 static value *
e4e640
-alloc_literal_str(program &p, insn_inserter &ins, std::string &str)
e4e640
+alloc_literal_str(program &p, insn_inserter &ins, value *s)
e4e640
 {
e4e640
+  std::string str = s->str();
e4e640
+
e4e640
+  size_t str_bytes = str.size() + 1;
e4e640
+  str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data
e4e640
+
e4e640
+  int ofs; size_t tmp_space;
e4e640
+
e4e640
   // Append the string to existing temporary data.
e4e640
   //
e4e640
   // TODO: This could produce significant space limitations.
e4e640
   // A better solution would be to integrate with the
e4e640
   // register allocator and reclaim the space after
e4e640
   // the string literal is no longer live.
e4e640
-  size_t tmp_space = p.max_tmp_space;
e4e640
+  tmp_space = p.max_tmp_space;
e4e640
   tmp_space += 4 - tmp_space % 4; // write aligned words to avoid verifier error
e4e640
   p.use_tmp_space(tmp_space);
e4e640
 
e4e640
-  size_t str_bytes = str.size() + 1;
e4e640
-  str_bytes += 4 - str_bytes % 4; // write aligned words to avoid garbage data
e4e640
   if (tmp_space + str_bytes > MAX_BPF_STACK)
e4e640
     throw std::runtime_error("string allocation failed due to lack of room on stack");
e4e640
 
e4e640
   tmp_space += str_bytes;
e4e640
 
e4e640
 #if 1
e4e640
+  // The following aren't ideal because an unlucky ordering of
e4e640
+  // allocation requests will waste additional space.
e4e640
+
e4e640
   // XXX PR23860: Passing a short (non-padded) string constant can fail
e4e640
   // the verifier, which is not smart enough to determine that accesses
e4e640
   // past the end of the string will never occur. To fix this, make sure
e4e640
   // the string offset is at least -BPF_MAXSTRINGLEN.
e4e640
-  //
e4e640
-  // Not ideal because an unlucky ordering of allocations may waste space.
e4e640
-  if (tmp_space < BPF_MAXSTRINGLEN)
e4e640
-    tmp_space = BPF_MAXSTRINGLEN;
e4e640
+  //if (!s->is_format() && tmp_space < BPF_MAXSTRINGLEN)
e4e640
+  //  tmp_space = BPF_MAXSTRINGLEN;
e4e640
+
e4e640
+  // TODO PR23860: An even uglier workaround for emit_string_copy()
e4e640
+  // overlapping source and destination regions. Only do this for
e4e640
+  // non-format strings, as format strings are not manipulated by the
e4e640
+  // eBPF program.
e4e640
+  if (!s->is_format() && tmp_space < BPF_MAXSTRINGLEN * 2 + str_bytes)
e4e640
+    tmp_space = BPF_MAXSTRINGLEN * 2 + str_bytes;
e4e640
 #endif
e4e640
 
e4e640
   p.use_tmp_space(tmp_space);
e4e640
-  int ofs = -tmp_space;
e4e640
+  ofs = -tmp_space;
e4e640
 
e4e640
   value *frame = p.lookup_reg(BPF_REG_10);
e4e640
   value *out = emit_simple_literal_str(p, ins, frame, ofs, str, false /* don't zero pad */);
e4e640
@@ -73,7 +86,7 @@ lower_str_values(program &p)
e4e640
             {
e4e640
               insn_before_inserter ins(b, j, "str");
e4e640
               std::string str0 = s0->str();
e4e640
-              value *new_s0 = alloc_literal_str(p, ins, str0);
e4e640
+              value *new_s0 = alloc_literal_str(p, ins, s0);
e4e640
               j->src0 = new_s0;
e4e640
             }
e4e640
 
e4e640
@@ -82,7 +95,7 @@ lower_str_values(program &p)
e4e640
             {
e4e640
               insn_before_inserter ins(b, j, "str");
e4e640
               std::string str1 = s1->str();
e4e640
-              value *new_s1 = alloc_literal_str(p, ins, str1);
e4e640
+              value *new_s1 = alloc_literal_str(p, ins, s1);
e4e640
               j->src1 = new_s1;
e4e640
             }
e4e640
         }
e4e640
diff --git a/bpf-translate.cxx b/bpf-translate.cxx
e4e640
index 0181380b7..20cd47032 100644
e4e640
--- a/bpf-translate.cxx
e4e640
+++ b/bpf-translate.cxx
e4e640
@@ -2568,6 +2568,9 @@ emit_simple_literal_str(program &this_prog, insn_inserter &this_ins,
e4e640
 // dest[+ofs] in 4-byte chunks, with optional zero-padding up to
e4e640
 // BPF_MAXSTRINGLEN.
e4e640
 //
e4e640
+// TODO (PR23860): This code does not work when the source and target
e4e640
+// regions overlap.
e4e640
+//
e4e640
 // ??? Could use 8-byte chunks if we're starved for instruction count.
e4e640
 // ??? Endianness of the target may come into play here.
e4e640
 value *
e4e640
@@ -2583,7 +2586,7 @@ bpf_unparser::emit_string_copy(value *dest, int ofs, value *src, bool zero_pad)
e4e640
     }
e4e640
 
e4e640
 #ifdef DEBUG_CODEGEN
e4e640
-  this_ins.notes.push("strcpy");
e4e640
+  this_ins.notes.push(zero_pad ? "strcpy_zero_pad" : "strcpy");
e4e640
 #endif
e4e640
 
e4e640
   size_t str_bytes = BPF_MAXSTRINGLEN;
e4e640
@@ -2866,7 +2869,7 @@ bpf_unparser::emit_print_format (const std::string& format,
e4e640
   // bpf program stack.  This is handled by bpf-opt.cxx lowering STR values.
e4e640
   size_t format_bytes = format.size() + 1;
e4e640
   this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
e4e640
-                   this_prog.new_str(format));
e4e640
+                   this_prog.new_str(format, true /*format_str*/));
e4e640
   emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
e4e640
   for (size_t i = 0; i < nargs; ++i)
e4e640
     emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
e4e640
-- 
e4e640
2.14.5
e4e640