cfi.c 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Clang Control Flow Integrity (CFI) support.
  4. *
  5. * Copyright (C) 2022 Google LLC
  6. */
  7. #include <asm/cfi.h>
  8. #include <asm/insn.h>
  9. #include <asm/insn-eval.h>
  10. #include <linux/string.h>
  11. /*
  12. * Returns the target address and the expected type when regs->ip points
  13. * to a compiler-generated CFI trap.
  14. */
  15. static bool decode_cfi_insn(struct pt_regs *regs, unsigned long *target,
  16. u32 *type)
  17. {
  18. char buffer[MAX_INSN_SIZE];
  19. struct insn insn;
  20. int offset = 0;
  21. *target = *type = 0;
  22. /*
  23. * The compiler generates the following instruction sequence
  24. * for indirect call checks:
  25. *
  26. *   movl -<id>, %r10d ; 6 bytes
  27. * addl -4(%reg), %r10d ; 4 bytes
  28. * je .Ltmp1 ; 2 bytes
  29. * ud2 ; <- regs->ip
  30. * .Ltmp1:
  31. *
  32. * We can decode the expected type and the target address from the
  33. * movl/addl instructions.
  34. */
  35. if (copy_from_kernel_nofault(buffer, (void *)regs->ip - 12, MAX_INSN_SIZE))
  36. return false;
  37. if (insn_decode_kernel(&insn, &buffer[offset]))
  38. return false;
  39. if (insn.opcode.value != 0xBA)
  40. return false;
  41. *type = -(u32)insn.immediate.value;
  42. if (copy_from_kernel_nofault(buffer, (void *)regs->ip - 6, MAX_INSN_SIZE))
  43. return false;
  44. if (insn_decode_kernel(&insn, &buffer[offset]))
  45. return false;
  46. if (insn.opcode.value != 0x3)
  47. return false;
  48. /* Read the target address from the register. */
  49. offset = insn_get_modrm_rm_off(&insn, regs);
  50. if (offset < 0)
  51. return false;
  52. *target = *(unsigned long *)((void *)regs + offset);
  53. return true;
  54. }
  55. /*
  56. * Checks if a ud2 trap is because of a CFI failure, and handles the trap
  57. * if needed. Returns a bug_trap_type value similarly to report_bug.
  58. */
  59. enum bug_trap_type handle_cfi_failure(struct pt_regs *regs)
  60. {
  61. unsigned long target;
  62. u32 type;
  63. if (!is_cfi_trap(regs->ip))
  64. return BUG_TRAP_TYPE_NONE;
  65. if (!decode_cfi_insn(regs, &target, &type))
  66. return report_cfi_failure_noaddr(regs, regs->ip);
  67. return report_cfi_failure(regs, regs->ip, &target, type);
  68. }
  69. /*
  70. * Ensure that __kcfi_typeid_ symbols are emitted for functions that may
  71. * not be indirectly called with all configurations.
  72. */
  73. __ADDRESSABLE(__memcpy)