Googler | b48fa91 | 2023-03-17 12:40:29 +0530 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2015-2016, 2020 The Linux Foundation. All rights reserved. |
| 3 | * |
| 4 | * Permission to use, copy, modify, and/or distribute this software for any |
| 5 | * purpose with or without fee is hereby granted, provided that the above |
| 6 | * copyright notice and this permission notice appear in all copies. |
| 7 | * |
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 15 | */ |
| 16 | |
| 17 | #include <asm/stacktrace.h> |
| 18 | #include <asm/current.h> |
| 19 | #include <linux/sched.h> |
| 20 | #include <linux/module.h> |
| 21 | #include <linux/smp.h> |
| 22 | |
| 23 | #include "skbuff_debug.h" |
| 24 | #include "skbuff_notifier.h" |
| 25 | #include "skbuff_recycle.h" |
| 26 | |
| 27 | static int skbuff_debugobj_enabled __read_mostly = 1; |
| 28 | |
| 29 | static int skbuff_debug_event_handler(struct notifier_block *nb, |
| 30 | unsigned long action, void *data); |
| 31 | static struct notifier_block skbuff_debug_notify = { |
| 32 | .notifier_call = skbuff_debug_event_handler, |
| 33 | .priority = 0 |
| 34 | }; |
| 35 | |
| 36 | inline u32 skbuff_debugobj_sum(struct sk_buff *skb) |
| 37 | { |
| 38 | int pos = offsetof(struct sk_buff, free_addr); |
| 39 | u32 sum = 0; |
| 40 | |
| 41 | while (pos--) |
| 42 | sum += ((u8 *)skb)[pos]; |
| 43 | |
| 44 | return sum; |
| 45 | } |
| 46 | |
| 47 | struct skbuff_debugobj_walking { |
| 48 | int pos; |
| 49 | void **d; |
| 50 | }; |
| 51 | |
| 52 | static int skbuff_debugobj_walkstack(struct stackframe *frame, void *p) |
| 53 | { |
| 54 | struct skbuff_debugobj_walking *w = (struct skbuff_debugobj_walking *)p; |
| 55 | unsigned long pc = frame->pc; |
| 56 | |
| 57 | if (w->pos < DEBUG_OBJECTS_SKBUFF_STACKSIZE - 1) { |
| 58 | w->d[w->pos++] = (void *)pc; |
| 59 | return 0; |
| 60 | } |
| 61 | |
| 62 | return -ENOENT; |
| 63 | } |
| 64 | |
| 65 | #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) |
| 66 | static void skbuff_debugobj_get_stack(void **ret) |
| 67 | { |
| 68 | struct stackframe frame; |
| 69 | |
| 70 | register unsigned long current_sp asm ("sp"); |
| 71 | struct skbuff_debugobj_walking w = {0, ret}; |
| 72 | void *p = &w; |
| 73 | |
| 74 | #ifdef CONFIG_ARM |
| 75 | frame.fp = (unsigned long)__builtin_frame_address(0); |
| 76 | frame.lr = (unsigned long)__builtin_return_address(0); |
| 77 | frame.sp = current_sp; |
| 78 | frame.pc = (unsigned long)skbuff_debugobj_get_stack; |
| 79 | #endif |
| 80 | |
| 81 | #ifdef CONFIG_ARM64 |
| 82 | start_backtrace(&frame, (unsigned long)__builtin_frame_address(0), (unsigned long)skbuff_debugobj_get_stack); |
| 83 | walk_stackframe(current, &frame, skbuff_debugobj_walkstack, p); |
| 84 | #else |
| 85 | walk_stackframe(&frame, skbuff_debugobj_walkstack, p); |
| 86 | #endif |
| 87 | ret[w.pos] = NULL; |
| 88 | } |
| 89 | #else |
| 90 | #error |
| 91 | static void skbuff_debugobj_get_stack(void **ret) |
| 92 | { |
| 93 | /* not supported */ |
| 94 | ret[0] = 0xdeadbeef; |
| 95 | } |
| 96 | #endif |
| 97 | |
| 98 | void skbuff_debugobj_print_stack(void *const *stack) |
| 99 | { |
| 100 | int i; |
| 101 | |
| 102 | for (i = 0; stack[i]; i++) |
| 103 | pr_emerg("\t %pS (0x%p)\n", stack[i], stack[i]); |
| 104 | } |
| 105 | |
| 106 | static const char *skbuff_debugobj_state_name(const struct sk_buff *skb) |
| 107 | { |
| 108 | int obj_state; |
| 109 | |
| 110 | obj_state = debug_object_get_state((struct sk_buff *)skb); |
| 111 | switch (obj_state) { |
| 112 | case ODEBUG_STATE_NONE: |
| 113 | return "none"; |
| 114 | case ODEBUG_STATE_INIT: |
| 115 | return "init"; |
| 116 | case ODEBUG_STATE_INACTIVE: |
| 117 | return "inactive"; |
| 118 | case ODEBUG_STATE_ACTIVE: |
| 119 | return "active"; |
| 120 | case ODEBUG_STATE_DESTROYED: |
| 121 | return "destroyed"; |
| 122 | case ODEBUG_STATE_NOTAVAILABLE: |
| 123 | return "not available"; |
| 124 | default: |
| 125 | return "invalid"; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | void skbuff_debugobj_print_skb(const struct sk_buff *skb) |
| 130 | { |
| 131 | pr_emerg("skb_debug: current process = %s (pid %i)\n", |
| 132 | current->comm, current->pid); |
| 133 | pr_emerg("skb_debug: skb 0x%p, next 0x%p, prev 0x%p, state = %s\n", skb, |
| 134 | skb->next, skb->prev, skbuff_debugobj_state_name(skb)); |
| 135 | pr_emerg("skb_debug: free stack:\n"); |
| 136 | skbuff_debugobj_print_stack(skb->free_addr); |
| 137 | pr_emerg("skb_debug: alloc stack:\n"); |
| 138 | skbuff_debugobj_print_stack(skb->alloc_addr); |
| 139 | } |
| 140 | EXPORT_SYMBOL(skbuff_debugobj_print_skb); |
| 141 | |
| 142 | /* skbuff_debugobj_fixup(): |
| 143 | * Called when an error is detected in the state machine for |
| 144 | * the objects |
| 145 | */ |
| 146 | static bool skbuff_debugobj_fixup(void *addr, enum debug_obj_state state) |
| 147 | { |
| 148 | struct sk_buff *skb = (struct sk_buff *)addr; |
| 149 | ftrace_dump(DUMP_ALL); |
| 150 | WARN(1, "skb_debug: state = %d, skb = 0x%p sum = %d (now %d)\n", |
| 151 | state, skb, skb->sum, skbuff_debugobj_sum(skb)); |
| 152 | skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_FSM, skb); |
| 153 | |
| 154 | return true; |
| 155 | } |
| 156 | |
| 157 | static struct debug_obj_descr skbuff_debug_descr = { |
| 158 | .name = "sk_buff_struct", |
| 159 | .fixup_init = skbuff_debugobj_fixup, |
| 160 | .fixup_activate = skbuff_debugobj_fixup, |
| 161 | .fixup_destroy = skbuff_debugobj_fixup, |
| 162 | .fixup_free = skbuff_debugobj_fixup, |
| 163 | }; |
| 164 | |
| 165 | inline void skbuff_debugobj_activate(struct sk_buff *skb) |
| 166 | { |
| 167 | int ret = 0; |
| 168 | |
| 169 | if (!skbuff_debugobj_enabled) |
| 170 | return; |
| 171 | |
| 172 | skbuff_debugobj_get_stack(skb->alloc_addr); |
| 173 | ret = debug_object_activate(skb, &skbuff_debug_descr); |
| 174 | if (ret) |
| 175 | goto err_act; |
| 176 | |
| 177 | skbuff_debugobj_sum_validate(skb); |
| 178 | |
| 179 | return; |
| 180 | |
| 181 | err_act: |
| 182 | ftrace_dump(DUMP_ALL); |
| 183 | WARN(1, "skb_debug: failed to activate err = %d skb = 0x%p sum = %d (now %d)\n", |
| 184 | ret, skb, skb->sum, skbuff_debugobj_sum(skb)); |
| 185 | skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_DBLALLOC, skb); |
| 186 | } |
| 187 | |
| 188 | inline void skbuff_debugobj_init_and_activate(struct sk_buff *skb) |
| 189 | { |
| 190 | if (!skbuff_debugobj_enabled) |
| 191 | return; |
| 192 | |
| 193 | /* if we're coming from the slab, the skb->sum might |
| 194 | * be invalid anyways |
| 195 | */ |
| 196 | skb->sum = skbuff_debugobj_sum(skb); |
| 197 | |
| 198 | debug_object_init(skb, &skbuff_debug_descr); |
| 199 | skbuff_debugobj_activate(skb); |
| 200 | } |
| 201 | |
| 202 | inline void skbuff_debugobj_deactivate(struct sk_buff *skb) |
| 203 | { |
| 204 | int obj_state; |
| 205 | |
| 206 | if (!skbuff_debugobj_enabled) |
| 207 | return; |
| 208 | |
| 209 | skb->sum = skbuff_debugobj_sum(skb); |
| 210 | |
| 211 | obj_state = debug_object_get_state(skb); |
| 212 | |
| 213 | if (obj_state == ODEBUG_STATE_ACTIVE) { |
| 214 | debug_object_deactivate(skb, &skbuff_debug_descr); |
| 215 | skbuff_debugobj_get_stack(skb->free_addr); |
| 216 | return; |
| 217 | } |
| 218 | |
| 219 | ftrace_dump(DUMP_ALL); |
| 220 | WARN(1, "skb_debug: deactivating inactive object skb=0x%p state=%d sum = %d (now %d)\n", |
| 221 | skb, obj_state, skb->sum, skbuff_debugobj_sum(skb)); |
| 222 | skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_DBLFREE, skb); |
| 223 | } |
| 224 | |
| 225 | inline void _skbuff_debugobj_sum_validate(struct sk_buff *skb, |
| 226 | const char *var, const char *src, |
| 227 | int line, const char *fxn) |
| 228 | { |
| 229 | if (!skbuff_debugobj_enabled || !skb) |
| 230 | return; |
| 231 | |
| 232 | if (skb->sum == skbuff_debugobj_sum(skb)) |
| 233 | return; |
| 234 | |
| 235 | ftrace_dump(DUMP_ALL); |
| 236 | WARN(1, "skb_debug: skb sum changed skb = 0x%p sum = %d (now %d)\n", |
| 237 | skb, skb->sum, skbuff_debugobj_sum(skb)); |
| 238 | pr_emerg("skb_debug: %s() checking %s in %s:%d\n", fxn, var, src, line); |
| 239 | skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_SUMERR, skb); |
| 240 | } |
| 241 | |
| 242 | inline void skbuff_debugobj_sum_update(struct sk_buff *skb) |
| 243 | { |
| 244 | if (!skbuff_debugobj_enabled || !skb) |
| 245 | return; |
| 246 | |
| 247 | skb->sum = skbuff_debugobj_sum(skb); |
| 248 | } |
| 249 | |
| 250 | inline void skbuff_debugobj_destroy(struct sk_buff *skb) |
| 251 | { |
| 252 | if (!skbuff_debugobj_enabled) |
| 253 | return; |
| 254 | |
| 255 | debug_object_destroy(skb, &skbuff_debug_descr); |
| 256 | } |
| 257 | |
| 258 | static int __init disable_object_debug(char *str) |
| 259 | { |
| 260 | skbuff_debugobj_enabled = 0; |
| 261 | |
| 262 | pr_info("skb_debug: debug objects is disabled\n"); |
| 263 | return 0; |
| 264 | } |
| 265 | |
| 266 | early_param("no_skbuff_debug_objects", disable_object_debug); |
| 267 | |
| 268 | void skbuff_debugobj_print_skb_list(const struct sk_buff *skb_list, |
| 269 | const char *list_title, int cpu) |
| 270 | { |
| 271 | int count; |
| 272 | struct sk_buff *skb_i = (struct sk_buff *)skb_list; |
| 273 | u32 sum_i, sum_now; |
| 274 | int obj_state; |
| 275 | |
| 276 | if (cpu < 0) { |
| 277 | cpu = get_cpu(); |
| 278 | put_cpu(); |
| 279 | } |
| 280 | pr_emerg("skb_debug: start skb list '%s' [CPU#%d]\n", list_title, cpu); |
| 281 | count = 0; |
| 282 | if (skb_list) { |
| 283 | do { |
| 284 | obj_state = |
| 285 | debug_object_get_state(skb_i); |
| 286 | if (obj_state < ODEBUG_STATE_NOTAVAILABLE) { |
| 287 | sum_i = skb_i->sum; |
| 288 | sum_now = skbuff_debugobj_sum(skb_i); |
| 289 | } else { |
| 290 | sum_i = 0; |
| 291 | sum_now = 0; |
| 292 | } |
| 293 | if (sum_i != sum_now) { |
| 294 | pr_emerg("skb_debug: [%02d] skb 0x%p, next 0x%p, prev 0x%p, state %d (%s), sum %d (now %d)\n", |
| 295 | count, skb_i, skb_i->next, skb_i->prev, |
| 296 | obj_state, skbuff_debugobj_state_name(skb_i), |
| 297 | sum_i, sum_now); |
| 298 | } |
| 299 | skb_i = skb_i->next; |
| 300 | count++; |
| 301 | } while (skb_list != skb_i); |
| 302 | } |
| 303 | pr_emerg("skb_debug: end skb list '%s'. In total %d skbs iterated.\n", list_title, count); |
| 304 | } |
| 305 | |
| 306 | void skbuff_debugobj_register_callback(void) |
| 307 | { |
| 308 | skb_recycler_notifier_register(&skbuff_debug_notify); |
| 309 | } |
| 310 | |
| 311 | int skbuff_debug_event_handler(struct notifier_block *nb, unsigned long action, |
| 312 | void *data) |
| 313 | { |
| 314 | struct sk_buff *skb = (struct sk_buff *)data; |
| 315 | |
| 316 | pr_emerg("skb_debug: notifier event %lu\n", action); |
| 317 | skbuff_debugobj_print_skb(skb); |
| 318 | skb_recycler_print_all_lists(); |
| 319 | |
| 320 | return NOTIFY_DONE; |
| 321 | } |