From 06351f7f372c670a630e7595fcd77913ed05742d Mon Sep 17 00:00:00 2001 From: Serhei Makarov Date: Thu, 8 Nov 2018 16:40:40 -0500 Subject: [PATCH 25/32] PR23860: additional stack protection for strings Fixes for verifier rejection of some cases requiring string copy, since the verifier would reject string copy code extending beyond the end of the string even if it was not reachable. * bpf-opt.cxx (alloc_literal_str): make sure the offset for a short string is at least BPF_MAXSTRINGLEN. (zero_stack): New function. (program::generate): Use zero_stack() to zero temporary area and prevent verifier complaints. * testsuite/systemtap.bpf/asm_tests/pr23860.stp: New testcase. --- bpf-opt.cxx | 32 ++++++++++++++++++++++++++- testsuite/systemtap.bpf/asm_tests/pr23860.stp | 24 ++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 testsuite/systemtap.bpf/asm_tests/pr23860.stp diff --git a/bpf-opt.cxx b/bpf-opt.cxx index 8b9a6ea60..089dcde55 100644 --- a/bpf-opt.cxx +++ b/bpf-opt.cxx @@ -36,7 +36,19 @@ alloc_literal_str(program &p, insn_inserter &ins, std::string &str) if (tmp_space + str_bytes > MAX_BPF_STACK) throw std::runtime_error("string allocation failed due to lack of room on stack"); - tmp_space += str_bytes; // TODO: round up for safety? + tmp_space += str_bytes; + +#if 1 + // XXX PR23860: Passing a short (non-padded) string constant can fail + // the verifier, which is not smart enough to determine that accesses + // past the end of the string will never occur. To fix this, make sure + // the string offset is at least -BPF_MAXSTRINGLEN. + // + // Not ideal because an unlucky ordering of allocations may waste space. + if (tmp_space < BPF_MAXSTRINGLEN) + tmp_space = BPF_MAXSTRINGLEN; +#endif + p.use_tmp_space(tmp_space); int ofs = -tmp_space; @@ -932,19 +944,37 @@ post_alloc_cleanup (program &p) } } +// XXX PR23860: Passing a short (non-padded) string constant can fail +// the verifier, which is not smart enough to determine that accesses +// past the end of the string will never occur. To fix this, start the +// program with some code to zero out the temporary stack space. +void +zero_stack(program &p) +{ + block *entry_block = p.blocks[0]; + insn_before_inserter ins(entry_block, entry_block->first, "zero_stack"); + value *frame = p.lookup_reg(BPF_REG_10); + for (int32_t ofs = -(int32_t)p.max_tmp_space; ofs < 0; ofs += 4) + p.mk_st(ins, BPF_W, frame, (int32_t)ofs, p.new_imm(0)); +} + void program::generate() { #ifdef DEBUG_CODEGEN std::cerr << "DEBUG BEFORE OPT " << *this << std::endl; #endif + lower_str_values(*this); + zero_stack(*this); + fixup_operands(*this); thread_jumps(*this); fold_jumps(*this); reorder_blocks(*this); reg_alloc(*this); post_alloc_cleanup(*this); + #ifdef DEBUG_CODEGEN std::cerr << "DEBUG AFTER OPT " << *this << std::endl; #endif diff --git a/testsuite/systemtap.bpf/asm_tests/pr23860.stp b/testsuite/systemtap.bpf/asm_tests/pr23860.stp new file mode 100644 index 000000000..bd3e05f66 --- /dev/null +++ b/testsuite/systemtap.bpf/asm_tests/pr23860.stp @@ -0,0 +1,24 @@ +function foo:string () +%{ /* bpf */ /* pure */ /* unprivileged */ /* stable */ + 0xbf, $$, "key", -, -; /* mov $$, "key" */ +%} + +function bar:string () +{ + return "kez" +} + +global t + +probe begin { + t[foo()] = 4 + t[bar()] = 6 + printf("U t[key]=%d, t[kez]=%d should be 4,6\n", t["key"], t["kez"]) +} + +probe kernel.function("vfs_read") { + t[foo()] = 5 + t[bar()] = 7 + printf("K t[key]=%d, t[kez]=%d should be 5,7\n", t["key"], t["kez"]) + exit() +} -- 2.14.5