Hi Jens,
This one-patch series fixes a page-pin leak in register_shm_helper() after iov_iter_extract_pages() partially extracts an unaligned user buffer. The cleanup path undercounts the pinned pages by one and loses the final pin when it frees the page array.
The issue was reproduced on an x86_64 7.0.0-29-generic kernel with 4 KiB pages. On the unpatched kernel, each failing registration increased nr_foll_pin_acquired - nr_foll_pin_released by one.
The faulty code is also present in current mainline, linux-next, the TEE maintainer's next branch, and the supported 7.2.y, 7.1.y, 6.18.y, and 6.12.y branches.
Regards, Shukai
Shukai Ni (1): tee: fix page count in register_shm_helper() error path
drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
iov_iter_extract_pages() returns the number of bytes extracted and sets off to the offset into the first page. For the iterator, the number of pages pinned is therefore DIV_ROUND_UP(len + off, PAGE_SIZE).
The partial-extraction error path instead records len / PAGE_SIZE. For an unaligned buffer this can undercount by one, causing unpin_user_pages() to leave a page pinned.
Fixes: d5cf5b37064b ("tee: fix register_shm_helper()") Cc: stable@vger.kernel.org Co-developed-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Shukai Ni shukai.ni@kuleuven.be --- drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 6742b35..fa446e4 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -454,7 +454,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, * If we only got a few pages, update to release the * correct amount below. */ - shm->num_pages = len / PAGE_SIZE; + shm->num_pages = DIV_ROUND_UP(len + off, PAGE_SIZE); ret = ERR_PTR(-ENOMEM); goto err_put_shm_pages; }
Hi,
On Mon, Aug 31, 2026 at 12:58 PM Shukai Ni shukai.ni@kuleuven.be wrote:
iov_iter_extract_pages() returns the number of bytes extracted and sets off to the offset into the first page. For the iterator, the number of pages pinned is therefore DIV_ROUND_UP(len + off, PAGE_SIZE).
The partial-extraction error path instead records len / PAGE_SIZE. For an unaligned buffer this can undercount by one, causing unpin_user_pages() to leave a page pinned.
Fixes: d5cf5b37064b ("tee: fix register_shm_helper()") Cc: stable@vger.kernel.org Co-developed-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Shukai Ni shukai.ni@kuleuven.be
drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 6742b35..fa446e4 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -454,7 +454,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, * If we only got a few pages, update to release the * correct amount below. */
shm->num_pages = len / PAGE_SIZE;
shm->num_pages = DIV_ROUND_UP(len + off, PAGE_SIZE); ret = ERR_PTR(-ENOMEM); goto err_put_shm_pages; }-- 2.55.0
Looks good. Sumit, do you agree?
Cheers, Jens
On Tue, 03 Nov 2026 at 19:11:06 +0100, Jens Wiklander wrote:
Hi,
On Mon, Aug 31, 2026 at 12:58 PM Shukai Ni shukai.ni@kuleuven.be wrote:
iov_iter_extract_pages() returns the number of bytes extracted and sets off to the offset into the first page. For the iterator, the number of pages pinned is therefore DIV_ROUND_UP(len + off, PAGE_SIZE).
The partial-extraction error path instead records len / PAGE_SIZE. For an unaligned buffer this can undercount by one, causing unpin_user_pages() to leave a page pinned.
Fixes: d5cf5b37064b ("tee: fix register_shm_helper()") Cc: stable@vger.kernel.org Co-developed-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Shukai Ni shukai.ni@kuleuven.be
drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 6742b35..fa446e4 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -454,7 +454,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, * If we only got a few pages, update to release the * correct amount below. */
shm->num_pages = len / PAGE_SIZE;
shm->num_pages = DIV_ROUND_UP(len + off, PAGE_SIZE); ret = ERR_PTR(-ENOMEM); goto err_put_shm_pages; }-- 2.55.0
Looks good. Sumit, do you agree?
Sounds like a reasonable fix to me, feel free to add:
Reviewed-by: Sumit Garg sumit.garg@oss.qualcomm.com
-Sumit
On Thu, Nov 5, 2026 at 1:16 PM Sumit Garg sumit.garg@kernel.org wrote:
On Tue, 03 Nov 2026 at 19:11:06 +0100, Jens Wiklander wrote:
Hi,
On Mon, Aug 31, 2026 at 12:58 PM Shukai Ni shukai.ni@kuleuven.be wrote:
iov_iter_extract_pages() returns the number of bytes extracted and sets off to the offset into the first page. For the iterator, the number of pages pinned is therefore DIV_ROUND_UP(len + off, PAGE_SIZE).
The partial-extraction error path instead records len / PAGE_SIZE. For an unaligned buffer this can undercount by one, causing unpin_user_pages() to leave a page pinned.
Fixes: d5cf5b37064b ("tee: fix register_shm_helper()") Cc: stable@vger.kernel.org Co-developed-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Jo Van Bulck jo.vanbulck@cs.kuleuven.be Signed-off-by: Shukai Ni shukai.ni@kuleuven.be
drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 6742b35..fa446e4 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -454,7 +454,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, * If we only got a few pages, update to release the * correct amount below. */
shm->num_pages = len / PAGE_SIZE;
shm->num_pages = DIV_ROUND_UP(len + off, PAGE_SIZE); ret = ERR_PTR(-ENOMEM); goto err_put_shm_pages; }-- 2.55.0
Looks good. Sumit, do you agree?
Sounds like a reasonable fix to me, feel free to add:
Reviewed-by: Sumit Garg sumit.garg@oss.qualcomm.com
I'm picking this up.
Cheers, Jens
op-tee@lists.trustedfirmware.org