aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common/include/linux/scatterlist.h
diff options
context:
space:
mode:
authorAustin Shafer <ashafer@badland.io>2023-04-21 07:56:50 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2023-11-29 16:36:05 +0000
commit934b2489b951698adc5d4c3421fa66fb80be7052 (patch)
treed8680cd059646304c5bbc0f537a6a0faa31c77ab /sys/compat/linuxkpi/common/include/linux/scatterlist.h
parent88910886fa50a6eccdc7d263778143ddd68c61df (diff)
downloadsrc-934b2489b951698adc5d4c3421fa66fb80be7052.tar.gz
src-934b2489b951698adc5d4c3421fa66fb80be7052.zip
linuxkpi: Fix __sg_alloc_table_from_pages loop
Commit 3e0856b63fe0e375a0951e05c2ef98bb2ebd9421 updated __sg_alloc_table_from_pages to use the same API as linux, but modified the loop condition when going over the pages in a sg list. Part of the change included moving the sg_next call out of the for loop and into the body, which causes an off by one error when traversing the list. Since sg_next is called before the loop body it will skip the first element and read one past the last element. This caused panics when running PRIME with nvidia-drm as the off-by-one issue causes a NULL dereference. (cherry picked from commit 3f686532c9b4fd603d4ad2d3392232cff24188f4) Commit 3f686532c9b4 tried to fix an issue with not properly starting at the first page in the sg list to prevent a panic. This worked but with the side effect of incrementing "s" during the final iteration causing it to be NULL since the list had ended. In cases non-DEBUG kernels this causes a panic with drm-5.15, since "s" is NULL when we later pass it to sg_mark_end(). This change decouples the iteration sg from the return value so that it is never incremented past the final page in the chain. (cherry picked from commit 09b0401e91a92bcb58ea1873857b42f8211f660f)
Diffstat (limited to 'sys/compat/linuxkpi/common/include/linux/scatterlist.h')
-rw-r--r--sys/compat/linuxkpi/common/include/linux/scatterlist.h12
1 files changed, 9 insertions, 3 deletions
diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
index 17928f9c50e5..e462d5c649f1 100644
--- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h
+++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
@@ -343,7 +343,7 @@ __sg_alloc_table_from_pages(struct sg_table *sgt,
{
unsigned int i, segs, cur, len;
int rc;
- struct scatterlist *s;
+ struct scatterlist *s, *sg_iter;
#if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300
if (prv != NULL) {
@@ -377,11 +377,17 @@ __sg_alloc_table_from_pages(struct sg_table *sgt,
#endif
cur = 0;
- for (i = 0, s = sgt->sgl; i < sgt->orig_nents; i++) {
+ for_each_sg(sgt->sgl, sg_iter, sgt->orig_nents, i) {
unsigned long seg_size;
unsigned int j;
- s = sg_next(s);
+ /*
+ * We need to make sure that when we exit this loop "s" has the
+ * last sg in the chain so we can call sg_mark_end() on it.
+ * Only set this inside the loop since sg_iter will be iterated
+ * until it is NULL.
+ */
+ s = sg_iter;
len = 0;
for (j = cur + 1; j < count; ++j) {