[PATCH] fanotify: report full event length for FIONREAD

Yichong Chen posted 1 patch 2 months ago
fs/notify/fanotify/fanotify_user.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
[PATCH] fanotify: report full event length for FIONREAD
Posted by Yichong Chen 2 months ago
fanotify_ioctl(FIONREAD) reports the number of bytes available to read
from the event queue.  It currently accounts only FAN_EVENT_METADATA_LEN
for each queued event.

That underestimates events that carry additional information records, such
as FAN_REPORT_DFID_NAME events.  A userspace program that uses FIONREAD to
size its read buffer can receive a length that is smaller than the next
event.  Reading with that buffer then fails with -EINVAL, while a larger
buffer succeeds and reports a larger metadata.event_len.

Use fanotify_event_len() when summing queued events so FIONREAD includes
all info records.

Fixes: 5e469c830fdb ("fanotify: copy event fid info to user")
Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
---
 fs/notify/fanotify/fanotify_user.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 9ee373ff5840..33693886b7d8 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1150,11 +1150,13 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 {
 	struct fsnotify_group *group;
 	struct fsnotify_event *fsn_event;
+	unsigned int info_mode;
 	void __user *p;
 	int ret = -ENOTTY;
 	size_t send_len = 0;
 
 	group = file->private_data;
+	info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
 
 	p = (void __user *) arg;
 
@@ -1162,7 +1164,8 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 	case FIONREAD:
 		spin_lock(&group->notification_lock);
 		list_for_each_entry(fsn_event, &group->notification_list, list)
-			send_len += FAN_EVENT_METADATA_LEN;
+			send_len += fanotify_event_len(info_mode,
+						       FANOTIFY_E(fsn_event));
 		spin_unlock(&group->notification_lock);
 		ret = put_user(send_len, (int __user *) p);
 		break;
-- 
2.51.0
Re: [PATCH] fanotify: report full event length for FIONREAD
Posted by Jan Kara 2 months ago
On Fri 31-07-26 10:18:27, Yichong Chen wrote:
> fanotify_ioctl(FIONREAD) reports the number of bytes available to read
> from the event queue.  It currently accounts only FAN_EVENT_METADATA_LEN
> for each queued event.
> 
> That underestimates events that carry additional information records, such
> as FAN_REPORT_DFID_NAME events.  A userspace program that uses FIONREAD to
> size its read buffer can receive a length that is smaller than the next
> event.  Reading with that buffer then fails with -EINVAL, while a larger
> buffer succeeds and reports a larger metadata.event_len.
> 
> Use fanotify_event_len() when summing queued events so FIONREAD includes
> all info records.
> 
> Fixes: 5e469c830fdb ("fanotify: copy event fid info to user")
> Signed-off-by: Yichong Chen <chenyichong@uniontech.com>

Thanks! I've added the patch to my tree.

								Honza

> ---
>  fs/notify/fanotify/fanotify_user.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index 9ee373ff5840..33693886b7d8 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -1150,11 +1150,13 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
>  {
>  	struct fsnotify_group *group;
>  	struct fsnotify_event *fsn_event;
> +	unsigned int info_mode;
>  	void __user *p;
>  	int ret = -ENOTTY;
>  	size_t send_len = 0;
>  
>  	group = file->private_data;
> +	info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
>  
>  	p = (void __user *) arg;
>  
> @@ -1162,7 +1164,8 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
>  	case FIONREAD:
>  		spin_lock(&group->notification_lock);
>  		list_for_each_entry(fsn_event, &group->notification_list, list)
> -			send_len += FAN_EVENT_METADATA_LEN;
> +			send_len += fanotify_event_len(info_mode,
> +						       FANOTIFY_E(fsn_event));
>  		spin_unlock(&group->notification_lock);
>  		ret = put_user(send_len, (int __user *) p);
>  		break;
> -- 
> 2.51.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR