// SPDX-License-Identifier: GPL-2.0-or-later // // The string and memory primitives. See ldls/kpi/kpi.h for the provenance rule. // // These are the least interesting functions in the layer and the most // dangerous to leave out, because a stub for one of them does not crash -- // it silently does nothing, and the caller carries on with a buffer full of // whatever was there before. // // That is not hypothetical. `memcpy` was a logging stub while the receive path // was being brought up: the driver copied each frame into its skb, the copy // did not happen, and the stack was handed the right *length* of zeroes. Every // counter said packets were arriving and DHCP never completed. A crash would // have been easier to find. #include "core/types.h" #include "uapi/user.h" #include "kpi.h" void * kpi_memcpy(void *d, const void *s, unsigned long n) { unsigned char *a = (unsigned char *)d; const unsigned char *b = (const unsigned char *)s; while (n--) *a++ = *b++; return d; } void * kpi_memmove(void *d, const void *s, unsigned long n) { unsigned char *a = (unsigned char *)d; const unsigned char *b = (const unsigned char *)s; if (a == b || n == 0) return d; if (a < b) { while (n--) *a++ = *b++; } else { a += n; b += n; while (n--) *--a = *--b; } return d; } void * kpi_memset(void *d, int c, unsigned long n) { unsigned char *a = (unsigned char *)d; while (n--) *a++ = (unsigned char)c; return d; } int kpi_memcmp(const void *x, const void *y, unsigned long n) { const unsigned char *a = x, *b = y; while (n--) { if (*a != *b) return (int)*a - (int)*b; a++; b++; } return 0; } // A null string is survivable, and reported. // // A hosted driver reads names out of structures this host only partly fills // in, and the first one it found was `pci_name(pdev)` -- a pointer into a // `struct device` that had never been given one. Faulting inside strnlen tells // you nothing about which field was missing; answering zero and saying so // leaves the driver running and names the problem. static long kpi_nullstr; static int str_ok(const char *s, const char *who) { if (s) return 1; if (++kpi_nullstr <= 4) { kpi_log(" kpi: "); kpi_log(who); kpi_log(" on a null string -- some field of a struct this host fills in " "only partly\n"); } return 0; } unsigned long kpi_strlen(const char *s) { unsigned long n = 0; if (!str_ok(s, "strlen")) return 0; while (s[n]) n++; return n; } unsigned long kpi_strnlen(const char *s, unsigned long max) { unsigned long n = 0; if (!str_ok(s, "strnlen")) return 0; while (n < max && s[n]) n++; return n; } // sized_strscpy(dest, src, count) -- a bounded copy that always terminates and // reports the length copied, or -E2BIG when it did not fit. long kpi_sized_strscpy(char *d, const char *s, unsigned long n) { unsigned long i = 0; if (n == 0) return -7; /* -E2BIG */ if (!str_ok(s, "strscpy")) { d[0] = 0; return 0; } while (i + 1 < n && s[i]) { d[i] = s[i]; i++; } d[i] = 0; return s[i] ? -7 : (long)i; } // The io variants. On this platform a BAR is ordinary memory once mapped, so // these differ from their plain counterparts only in that the compiler must // not reorder or elide them -- which is what the volatile is for. void kpi_memcpy_fromio(void *d, const void *src, unsigned long n) { unsigned char *a = (unsigned char *)d; const volatile unsigned char *b = (const volatile unsigned char *)src; while (n--) *a++ = *b++; } void kpi_memcpy_toio(void *dst, const void *s, unsigned long n) { volatile unsigned char *a = (volatile unsigned char *)dst; const unsigned char *b = (const unsigned char *)s; while (n--) *a++ = *b++; } // __fortify_panic is what a driver's bounds-checked string helpers call when // they detect an overflow. Reaching it means the driver found a bug in itself, // and the honest thing is to say so rather than return. void (*kpi_where)(unsigned long addr); void kpi_fortify_panic(unsigned int reason, unsigned long avail, unsigned long size) { kpi_log("kpi: the driver called __fortify_panic -- it detected an " "overflow in itself\n"); // Which check, how much it thought it had, how much it wanted, and where // from -- the address is in the driver's own text, so subtract the load // address logged for it and look the offset up with nm. This took a // hardware round trip to find the last time it said nothing. kpi_log(" reason "); kpi_lognum(reason); kpi_log(", available "); kpi_lognum((long long)avail); kpi_log(", wanted "); kpi_lognum((long long)size); kpi_log(", from "); if (kpi_where) kpi_where((unsigned long)__builtin_return_address(0)); else { kpi_log("0x"); kpi_loghex((unsigned long)__builtin_return_address(0), 16); } kpi_log("\n"); // Not spin: a driver that has found a bug in itself holds /dev/netdev and // one core at 100% until reboot. The job is KeepAlive = no, so leaving is // free, and the kernel takes the interface back. exit_group(1); for (;;) ; }