mirror of
https://github.com/SCST-project/scst.git
synced 2026-10-22 14:12:33 +00:00
Extend the scst_user fault hook with a fork action: once armed, the handler fork()s before its next scst_user ioctl, and the child only sleeps, as a crash reporter, system() or popen() would. The new spec reads LBA 0 first, so the handler's cached buffer holds known data, then makes the handler fork. Afterwards READ CAPACITY(10) and (16), reads and writes must return the right geometry and data. The hook logs its success line only after the fault was applied, and logs a failed fork() with its errno. The specs fail on that line and require each fault's success line, so a failed fork() cannot pass as a handler that forked. Before Linux 5.9, the same boundary as dev_user_map_buf(), scst_user keeps get_user_pages() and a handler that forks must use MADV_DONTFORK, which fileio_tgt does not. There the hook applies MADV_DONTFORK to every active data buffer before it forks and logs how many it protected. It tracks the buffers from the handler's ALLOC_MEM and EXEC replies, and restores MADV_DOFORK before the handler frees one. The spec requires that line below 5.9 and its absence from 5.9, and the same correct geometry and data on every kernel. So 5.9 and later test the pin alone, and older kernels test the documented MADV_DONTFORK requirement. Without pinned buffers SCST keeps reading the pre-fork page, and the next READ CAPACITY reports the stale data block as its block length.
33 lines
788 B
Go
33 lines
788 B
Go
// SPDX-License-Identifier: GPL-2.0-only
|
|
package testhost
|
|
|
|
import "testing"
|
|
|
|
func TestKernelAtLeast(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
release string
|
|
want bool
|
|
}{
|
|
{"5.9", true},
|
|
{"5.9.0-fork59", true},
|
|
{"5.9.16", true},
|
|
{"5.10.264", true},
|
|
{"6.17.13-kunit", true},
|
|
{"10.0.1", true},
|
|
{"5.8.18", false},
|
|
{"5.4.0-200-generic", false},
|
|
{"4.18.0-553.el8_10.x86_64", false},
|
|
{"4.20.1", false},
|
|
} {
|
|
got, err := KernelAtLeast(tc.release, 5, 9)
|
|
if err != nil || got != tc.want {
|
|
t.Errorf("KernelAtLeast(%q, 5, 9) = %t, %v; want %t", tc.release, got, err, tc.want)
|
|
}
|
|
}
|
|
for _, release := range []string{"", "5", "abc", "v5.9"} {
|
|
if _, err := KernelAtLeast(release, 5, 9); err == nil {
|
|
t.Errorf("accepted kernel release %q", release)
|
|
}
|
|
}
|
|
}
|