SchOS/ldls/kpi/string.c
Nexoniarz 26bca020b6 licence: SchOS is GPL-2.0-or-later, no longer Apache-2.0
Nexoniarz is the sole author and SchOS was never published under
Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text,
every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE,
README.md, docs/licensing.md and the site say so. projects/schfs-linux
follows the rest of the tree.

The GPL gives copyleft: a fork has to ship its source. It also lifts the
old "Linux drivers, never" rule, since GPL-2.0-only code combines with
GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still
has to pick one version, so docs/licensing.md now keeps Apache-2.0 and
GPLv3 code out of the kernel to keep Linux code possible there. BSD
4-clause code goes from "avoid" to "no", because the advertising clause
is GPL-incompatible.

LDLS still ships no .ko. That boundary is now project policy rather than
a legal requirement, and ldls.md, devices.md and licensing.md section 6
say so. refmod declares license=GPL, and ldls-check passes (27 checks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:48:45 +02:00

145 lines
4 KiB
C

// 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 "types.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_fortify_panic(void)
{
kpi_log("kpi: the driver called __fortify_panic -- it detected an "
"overflow in itself\n");
for (;;)
;
}