From: Oliver Hartkopp socketcan@hartkopp.net
mainline inclusion from mainline-5.12-rc1 commit 0de70e287b44a0735273919c987313f021cccb72 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I4QAGM?from=project-issue
-------------------------------------------------
Multiple filters (struct can_filter) can be set with the setsockopt() function, which was originally intended as a write-only operation.
As getsockopt() also provides a CAN_RAW_FILTER option to read back the given filters, the caller has to provide an appropriate user space buffer. In the case this buffer is too small the getsockopt() silently truncates the filter information and gives no information about the needed space. This is safe but not convenient for the programmer.
In net/core/sock.c the SO_PEERGROUPS sockopt had a similar requirement and solved it by returning -ERANGE in the case that the provided data does not fit into the given user space buffer and fills the required size into optlen, so that the caller can retry with a matching buffer length.
This patch adopts this approach for CAN_RAW_FILTER getsockopt().
Reported-by: Phillip Schichtel phillip@schich.tel Signed-off-by: Oliver Hartkopp socketcan@hartkopp.net Tested-By: Phillip Schichtel phillip@schich.tel Link: https://lore.kernel.org/r/20201216174928.21663-1-socketcan@hartkopp.net Signed-off-by: Marc Kleine-Budde mkl@pengutronix.de Signed-off-by: Ziyang Xuan william.xuanziyang@huawei.com Reviewed-by: Yue Haibing yuehaibing@huawei.com Reviewed-by: Wei Yongjun weiyongjun1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- net/can/raw.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/net/can/raw.c b/net/can/raw.c index 069657f681af..5dca1e9e44cf 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -710,10 +710,18 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, if (ro->count > 0) { int fsize = ro->count * sizeof(struct can_filter);
- if (len > fsize) - len = fsize; - if (copy_to_user(optval, ro->filter, len)) - err = -EFAULT; + /* user space buffer to small for filter list? */ + if (len < fsize) { + /* return -ERANGE and needed space in optlen */ + err = -ERANGE; + if (put_user(fsize, optlen)) + err = -EFAULT; + } else { + if (len > fsize) + len = fsize; + if (copy_to_user(optval, ro->filter, len)) + err = -EFAULT; + } } else { len = 0; }