Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Model Checking (what may become part of) the BP...

shunghsiyu
November 12, 2023

Model Checking (what may become part of) the BPF Verifier

Using model checking to validate a new data structure and algorithms for value tracking within Linux Kernel's BPF verifier.

See https://hackweek.opensuse.org/23/projects/model-checking-the-bpf-verifier for more details.

shunghsiyu

November 12, 2023
Tweet

More Decks by shunghsiyu

Other Decks in Technology

Transcript

  1. Copyright © SUSE 10 NOVEMBER 20231 Model Checking (what may

    become part of) the BPF Verifier Hackweek 23, Shung-Hsi Yu
  2. Copyright © SUSE Allows users to supply code that will

    run in kernel context, useful for customizing system’s behavior towards user’s desire: — Networking — Tracing — Security BPF 2
  3. Copyright © SUSE — Project can be found on the

    Hack Week website — Use model checking to validate the tnum_add() algorithm – already done by the Sound, precise, and fast abstract interpretation with tristate numbers work – I’m just learning through mimicking Last year at Hack Week 21 Previous progress 3
  4. Copyright © SUSE — Re-open the same project — Implementing

    a more efficient way to do value tracking — Confirm that the new algorithms works with model checking — Submitted a draft PR to upstream – received initial feedback from Alexei Starovoitov (one of the maintainers of BPF subsystem) Hack Week 23 Progress so far this year 4
  5. Copyright © SUSE Check that user-supplied code does not: —

    Crash the kernel — Access data it is not suppose to read/write e.g. CVE-2021-3490 privilege escalation vulnerabilities Need to ensure that the BPF verifier is bug-free (as much as possible) BPF Verifier 6
  6. Copyright © SUSE int arr[10]; /* Is access() ever called

    with `i` greater than 10? */ int access(int i) { return arr[i]; /* access out-of-bound? */ } An example Value Tracking 7
  7. Copyright © SUSE int access(int i); /* `i` must NOT

    be greater than 10 */ void main(void) { int i; i = 1; /* `i` is surely 1 */ access(i); /* 1 < 10, so ok */ } An example Value Tracking 8
  8. Copyright © SUSE int access(int i); /* `i` must NOT

    be greater than 10 */ void main(void) { int i; i = 2023; /* `i` is surely 2023 */ access(i); /* 2023 > 10, so NOT ok */ } An example Value Tracking 9
  9. Copyright © SUSE static int i; /* global variable */

    void main(void) { /* `i` can be anything from INT_MIN to INT_MAX */ access(i); /* INT_MAX > 10, so NOT ok */ } An example Value Tracking 10
  10. Copyright © SUSE static int i; /* global variable */

    void main(void) { /* `i` can be anything from INT_MIN to INT_MAX */ if (0 < i && i < 2) /* `i` can be 0 to 2 */ access(i); /* 2 < 10, so ok */ } An example Value Tracking 11
  11. Copyright © SUSE static int i; /* global variable */

    void main(void) { /* `i` can be anything from INT_MIN to INT_MAX */ if (0 < i && i < 2) /* `i` can be 0 to 2 */ i = i + 5 /* `i` can be 5 to 7 */ access(i); /* 7 < 10, so ok */ } An example Value Tracking 12
  12. Copyright © SUSE static int i, j; /* global variable

    */ void main(void) { /* `i`, `j` can be anything from INT_MIN to INT_MAX */ if (0 < i && i < 2) /* `i` can be 0 to 2 */ if (0 < j && j < 4) /* `j` can be 0 to 4 */ /* i + j can be 0 to 6 */ access(i + j); /* 6 < 10, so ok */ } An example Value Tracking 13
  13. Copyright © SUSE BPF Verifier tracks the range of possible

    value similar to previous reasoning, by tracking possible value through minimum and maximum struct range { int min; int max; } In the Linux Kernel Value Tracking 14
  14. Copyright © SUSE … and implement corresponding operation that work

    on the range e.g. for addition we’d have a range_add() function /* Takes two range and adds them */ struct range range_add(struct range a, struct range b) { ... } In the Linux Kernel Value Tracking 15
  15. Copyright © SUSE … and implement corresponding operation that work

    on the range e.g. for addition we’d have a range_add() function /* Takes two range and adds them */ struct range range_add(struct range a, struct range b) { return (struct range){ .min=a.min+b.min, .max=a.max+b.max } } In the Linux Kernel Value Tracking 16
  16. Copyright © SUSE … and implement corresponding operation that work

    on the range e.g. for addition we’d have a range_add() function but does it work? /* Takes two range and adds them */ struct range range_add(struct range a, struct range b) { return (struct range){ .min=a.min+b.min, .max=a.max+b.max } } In the Linux Kernel Value Tracking 17 ?
  17. Copyright © SUSE Check with unit tests struct range a

    = { .min=0, .max=2 }; struct range b = { .min=0, .max=4 }; struct range r = range_add(a, b); assert(r.min == 0 && r.max == 6); Are There Bug(s)? 19
  18. Copyright © SUSE But we can’t cover all the possible

    cases it would take at least 232 cases to cover them all Are There Bug(s)? 20
  19. Copyright © SUSE Instead of testing case by case, try

    to check some abstract property Model Checking 21
  20. Copyright © SUSE To do so, first create a copy

    of algorithm using the modeling DSL Model Checking 22
  21. Copyright © SUSE To do so, first create a copy

    of algorithm using the modeling DSL Model Checking 23 struct range range_add(struct range a, struct range b) { return (struct range){ .min=a.min+b.min, .max=a.max+b.max } }
  22. Copyright © SUSE To do so, first create a copy

    of algorithm using the modeling DSL Model Checking 24 struct range range_add(struct range a, struct range b) { return (struct range){ .min=a.min+b.min, .max=a.max+b.max } } from z3 import * def range_add(a: Range, b: Range): # Python new_min = a.min + b.start new_max = a.max + b.end return range(min=new_start, max=new_max)
  23. Copyright © SUSE To do so, first create a copy

    of algorithm using the modeling DSL Model Checking 25 from z3 import * def range_add(a: Range, b: Range): # Python new_min = a.min + b.start new_max = a.max + b.end return range(min=new_start, max=new_max) struct range range_add(struct range a, struct range b) { return (struct range){ .min=a.min+b.min, .max=a.max+b.max } } C implementation
  24. Copyright © SUSE To do so, first create a copy

    of algorithm using the modeling DSL Model Checking 26 struct range range_add(struct range a, struct range b) { return (struct range){ .min=a.min+b.min, .max=a.max+b.max } } from z3 import * def range_add(a: Range, b: Range): # Python new_min = a.min + b.start new_max = a.max + b.end return range(min=new_start, max=new_max) “model” of the above implementation
  25. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 27
  26. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 28 model checking for range_add()
  27. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 29 say we have to integer x and y
  28. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 30 and two ranges, r1 and r2
  29. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 31 x can be any possible value within the r1 range and y can be any possible value within r2
  30. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 32 now if we calculate a new rsum using range_add()
  31. Copyright © SUSE x = Int('x'); y = Int('y') r1

    = Range('r1'); r2 = Range('r2') rsum = range_add(r1, r2) premise = And(r1.contains(x), r2.contains(y)) prove( Implies( premise, rsum.contains(x + y))) Model Checking 33 (x+y) should remain within rsum
  32. Copyright © SUSE © SUSE LLC. All Rights Reserved. SUSE

    and the SUSE logo are registered trademarks of SUSE LLC in the United States and other countries. All third-party trademarks are the property of their respective owners. For more information, contact SUSE at: +1 800 796 3700 (U.S./Canada) Frankenstrasse 146 90461 Nürnberg www.suse.com Thank you