Skip to content
This repository was archived by the owner on Oct 3, 2024. It is now read-only.

Commit baa4146

Browse files
jpoimboeIngo Molnar
authored andcommitted
objtool: Implement stack validation 2.0
This is a major rewrite of objtool. Instead of only tracking frame pointer changes, it now tracks all stack-related operations, including all register saves/restores. In addition to making stack validation more robust, this also paves the way for undwarf generation. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: live-patching@vger.kernel.org Link: http://lkml.kernel.org/r/678bd94c0566c6129bcc376cddb259c4c5633004.1498659915.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent c207aee commit baa4146

File tree

11 files changed

+1130
-320
lines changed

11 files changed

+1130
-320
lines changed

tools/objtool/Documentation/stack-validation.txt

Lines changed: 65 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -127,28 +127,13 @@ b) 100% reliable stack traces for DWARF enabled kernels
127127

128128
c) Higher live patching compatibility rate
129129

130-
(NOTE: This is not yet implemented)
131-
132-
Currently with CONFIG_LIVEPATCH there's a basic live patching
133-
framework which is safe for roughly 85-90% of "security" fixes. But
134-
patches can't have complex features like function dependency or
135-
prototype changes, or data structure changes.
136-
137-
There's a strong need to support patches which have the more complex
138-
features so that the patch compatibility rate for security fixes can
139-
eventually approach something resembling 100%. To achieve that, a
140-
"consistency model" is needed, which allows tasks to be safely
141-
transitioned from an unpatched state to a patched state.
142-
143-
One of the key requirements of the currently proposed livepatch
144-
consistency model [*] is that it needs to walk the stack of each
145-
sleeping task to determine if it can be transitioned to the patched
146-
state. If objtool can ensure that stack traces are reliable, this
147-
consistency model can be used and the live patching compatibility
148-
rate can be improved significantly.
149-
150-
[*] https://lkml.kernel.org/r/cover.1423499826.git.jpoimboe@redhat.com
130+
Livepatch has an optional "consistency model", which is needed for
131+
more complex patches. In order for the consistency model to work,
132+
stack traces need to be reliable (or an unreliable condition needs to
133+
be detectable). Objtool makes that possible.
151134

135+
For more details, see the livepatch documentation in the Linux kernel
136+
source tree at Documentation/livepatch/livepatch.txt.
152137

153138
Rules
154139
-----
@@ -201,105 +186,113 @@ To achieve the validation, objtool enforces the following rules:
201186
return normally.
202187

203188

204-
Errors in .S files
205-
------------------
189+
Objtool warnings
190+
----------------
206191

207-
If you're getting an error in a compiled .S file which you don't
208-
understand, first make sure that the affected code follows the above
209-
rules.
192+
For asm files, if you're getting an error which doesn't make sense,
193+
first make sure that the affected code follows the above rules.
194+
195+
For C files, the common culprits are inline asm statements and calls to
196+
"noreturn" functions. See below for more details.
197+
198+
Another possible cause for errors in C code is if the Makefile removes
199+
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
210200

211201
Here are some examples of common warnings reported by objtool, what
212202
they mean, and suggestions for how to fix them.
213203

214204

215-
1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
205+
1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
216206

217207
The func() function made a function call without first saving and/or
218-
updating the frame pointer.
219-
220-
If func() is indeed a callable function, add proper frame pointer
221-
logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, remove
222-
its ELF function annotation by changing ENDPROC to END.
208+
updating the frame pointer, and CONFIG_FRAME_POINTER is enabled.
223209

224-
If you're getting this error in a .c file, see the "Errors in .c
225-
files" section.
210+
If the error is for an asm file, and func() is indeed a callable
211+
function, add proper frame pointer logic using the FRAME_BEGIN and
212+
FRAME_END macros. Otherwise, if it's not a callable function, remove
213+
its ELF function annotation by changing ENDPROC to END, and instead
214+
use the manual CFI hint macros in asm/undwarf.h.
226215

216+
If it's a GCC-compiled .c file, the error may be because the function
217+
uses an inline asm() statement which has a "call" instruction. An
218+
asm() statement with a call instruction must declare the use of the
219+
stack pointer in its output operand. For example, on x86_64:
227220

228-
2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function
229-
230-
A return instruction was detected, but objtool couldn't find a way
231-
for a callable function to reach the instruction.
221+
register void *__sp asm("rsp");
222+
asm volatile("call func" : "+r" (__sp));
232223

233-
If the return instruction is inside (or reachable from) a callable
234-
function, the function needs to be annotated with the ENTRY/ENDPROC
235-
macros.
224+
Otherwise the stack frame may not get created before the call.
236225

237-
If you _really_ need a return instruction outside of a function, and
238-
are 100% sure that it won't affect stack traces, you can tell
239-
objtool to ignore it. See the "Adding exceptions" section below.
240226

227+
2. file.o: warning: objtool: .text+0x53: unreachable instruction
241228

242-
3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction
229+
Objtool couldn't find a code path to reach the instruction.
243230

244-
The instruction lives inside of a callable function, but there's no
245-
possible control flow path from the beginning of the function to the
246-
instruction.
231+
If the error is for an asm file, and the instruction is inside (or
232+
reachable from) a callable function, the function should be annotated
233+
with the ENTRY/ENDPROC macros (ENDPROC is the important one).
234+
Otherwise, the code should probably be annotated with the CFI hint
235+
macros in asm/undwarf.h so objtool and the unwinder can know the
236+
stack state associated with the code.
247237

248-
If the instruction is actually needed, and it's actually in a
249-
callable function, ensure that its function is properly annotated
250-
with ENTRY/ENDPROC.
238+
If you're 100% sure the code won't affect stack traces, or if you're
239+
a just a bad person, you can tell objtool to ignore it. See the
240+
"Adding exceptions" section below.
251241

252242
If it's not actually in a callable function (e.g. kernel entry code),
253243
change ENDPROC to END.
254244

255245

256-
4. asm_file.o: warning: objtool: func(): can't find starting instruction
246+
4. file.o: warning: objtool: func(): can't find starting instruction
257247
or
258-
asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction
248+
file.o: warning: objtool: func()+0x11dd: can't decode instruction
259249

260-
Did you put data in a text section? If so, that can confuse
250+
Does the file have data in a text section? If so, that can confuse
261251
objtool's instruction decoder. Move the data to a more appropriate
262252
section like .data or .rodata.
263253

264254

265-
5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction
266-
267-
This is a kernel entry/exit instruction like sysenter or sysret.
268-
Such instructions aren't allowed in a callable function, and are most
269-
likely part of the kernel entry code.
255+
5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function
270256

271-
If the instruction isn't actually in a callable function, change
272-
ENDPROC to END.
257+
This is a kernel entry/exit instruction like sysenter or iret. Such
258+
instructions aren't allowed in a callable function, and are most
259+
likely part of the kernel entry code. They should usually not have
260+
the callable function annotation (ENDPROC) and should always be
261+
annotated with the CFI hint macros in asm/undwarf.h.
273262

274263

275-
6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer
264+
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
276265

277-
This is a dynamic jump or a jump to an undefined symbol. Stacktool
266+
This is a dynamic jump or a jump to an undefined symbol. Objtool
278267
assumed it's a sibling call and detected that the frame pointer
279268
wasn't first restored to its original state.
280269

281270
If it's not really a sibling call, you may need to move the
282271
destination code to the local file.
283272

284273
If the instruction is not actually in a callable function (e.g.
285-
kernel entry code), change ENDPROC to END.
274+
kernel entry code), change ENDPROC to END and annotate manually with
275+
the CFI hint macros in asm/undwarf.h.
286276

287277

288-
7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch
278+
7. file: warning: objtool: func()+0x5c: stack state mismatch
289279

290280
The instruction's frame pointer state is inconsistent, depending on
291281
which execution path was taken to reach the instruction.
292282

293-
Make sure the function pushes and sets up the frame pointer (for
294-
x86_64, this means rbp) at the beginning of the function and pops it
295-
at the end of the function. Also make sure that no other code in the
296-
function touches the frame pointer.
283+
Make sure that, when CONFIG_FRAME_POINTER is enabled, the function
284+
pushes and sets up the frame pointer (for x86_64, this means rbp) at
285+
the beginning of the function and pops it at the end of the function.
286+
Also make sure that no other code in the function touches the frame
287+
pointer.
297288

289+
Another possibility is that the code has some asm or inline asm which
290+
does some unusual things to the stack or the frame pointer. In such
291+
cases it's probably appropriate to use the CFI hint macros in
292+
asm/undwarf.h.
298293

299-
Errors in .c files
300-
------------------
301294

302-
1. c_file.o: warning: objtool: funcA() falls through to next function funcB()
295+
8. file.o: warning: objtool: funcA() falls through to next function funcB()
303296

304297
This means that funcA() doesn't end with a return instruction or an
305298
unconditional jump, and that objtool has determined that the function
@@ -318,22 +311,6 @@ Errors in .c files
318311
might be corrupt due to a gcc bug. For more details, see:
319312
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
320313

321-
2. If you're getting any other objtool error in a compiled .c file, it
322-
may be because the file uses an asm() statement which has a "call"
323-
instruction. An asm() statement with a call instruction must declare
324-
the use of the stack pointer in its output operand. For example, on
325-
x86_64:
326-
327-
register void *__sp asm("rsp");
328-
asm volatile("call func" : "+r" (__sp));
329-
330-
Otherwise the stack frame may not get created before the call.
331-
332-
3. Another possible cause for errors in C code is if the Makefile removes
333-
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
334-
335-
Also see the above section for .S file errors for more information what
336-
the individual error messages mean.
337314

338315
If the error doesn't seem to make sense, it could be a bug in objtool.
339316
Feel free to ask the objtool maintainer for help.

tools/objtool/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
2525
all: $(OBJTOOL)
2626

2727
INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
28-
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
28+
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES)
2929
LDFLAGS += -lelf $(LIBSUBCMD)
3030

3131
# Allow old libelf to be used:

tools/objtool/arch.h

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,63 @@
1919
#define _ARCH_H
2020

2121
#include <stdbool.h>
22+
#include <linux/list.h>
2223
#include "elf.h"
24+
#include "cfi.h"
2325

24-
#define INSN_FP_SAVE 1
25-
#define INSN_FP_SETUP 2
26-
#define INSN_FP_RESTORE 3
27-
#define INSN_JUMP_CONDITIONAL 4
28-
#define INSN_JUMP_UNCONDITIONAL 5
29-
#define INSN_JUMP_DYNAMIC 6
30-
#define INSN_CALL 7
31-
#define INSN_CALL_DYNAMIC 8
32-
#define INSN_RETURN 9
33-
#define INSN_CONTEXT_SWITCH 10
34-
#define INSN_NOP 11
35-
#define INSN_OTHER 12
26+
#define INSN_JUMP_CONDITIONAL 1
27+
#define INSN_JUMP_UNCONDITIONAL 2
28+
#define INSN_JUMP_DYNAMIC 3
29+
#define INSN_CALL 4
30+
#define INSN_CALL_DYNAMIC 5
31+
#define INSN_RETURN 6
32+
#define INSN_CONTEXT_SWITCH 7
33+
#define INSN_STACK 8
34+
#define INSN_NOP 9
35+
#define INSN_OTHER 10
3636
#define INSN_LAST INSN_OTHER
3737

38+
enum op_dest_type {
39+
OP_DEST_REG,
40+
OP_DEST_REG_INDIRECT,
41+
OP_DEST_MEM,
42+
OP_DEST_PUSH,
43+
OP_DEST_LEAVE,
44+
};
45+
46+
struct op_dest {
47+
enum op_dest_type type;
48+
unsigned char reg;
49+
int offset;
50+
};
51+
52+
enum op_src_type {
53+
OP_SRC_REG,
54+
OP_SRC_REG_INDIRECT,
55+
OP_SRC_CONST,
56+
OP_SRC_POP,
57+
OP_SRC_ADD,
58+
OP_SRC_AND,
59+
};
60+
61+
struct op_src {
62+
enum op_src_type type;
63+
unsigned char reg;
64+
int offset;
65+
};
66+
67+
struct stack_op {
68+
struct op_dest dest;
69+
struct op_src src;
70+
};
71+
72+
void arch_initial_func_cfi_state(struct cfi_state *state);
73+
3874
int arch_decode_instruction(struct elf *elf, struct section *sec,
3975
unsigned long offset, unsigned int maxlen,
4076
unsigned int *len, unsigned char *type,
41-
unsigned long *displacement);
77+
unsigned long *immediate, struct stack_op *op);
78+
79+
bool arch_callee_saved_reg(unsigned char reg);
4280

4381
#endif /* _ARCH_H */

0 commit comments

Comments
 (0)