Bunch of unrelated fixes

- polling fix for trans fd that ought to have been fixed otherwise back
 in March, but apparently came back somewhere else...
 - USB transport buffer overflow fix
 - Some dentry lifetime rework to handle metadata update for currently
 opened files in uncached mode, or inode type change in cached mode
 - a double-put on invalid flush found by syzbot
 - and finally /sys/fs/9p/caches not advancing buffer and overwriting
 itself for large contents
 
 Thanks to everyone involved!
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/IPbcYBuWt0zoYhOq06b7GqY5nAFAmjns5cACgkQq06b7GqY
 5nDGLg/+Ltsiwj51CDwyJxFPX4ki1jM3j2iA8BNpdjjzCev6Jp1vuWBPiV+sTtgM
 zKmIw9joyvzaIXKzA3h7tMe7h7y8H4p7KZyD3YSDSNW5XIb5UYXeQwYVw39OxENk
 nVQkQ41H+bPepmz3R0t/F4la9myVLBtfdcPWJN+JfkOLgdeemCGf+TShJ6F+9Qaq
 PTd3UKfdv8JRlKQmeLH7pPlBoWYRoC2Tq3pZbyC3D1A50bWkTfxusW0k3MUzRyHO
 t/jNbhHt0ai+densQ6O5M+ALMI7KIIzR+obVaBHfzSUcwFeGhmW2x84Mo0qX6YMF
 0B/fY3mCXi8QO6my1hfwmbRtY5mEV967wM/uF7E/SuvyNVizOMBZn8FgWfVPVszr
 ix1iNBgQJdoOEMbHvDfCAtGNorFrMiybyLvoabL7YCwFo68LdYqj2br3/feajoYM
 5g46nUcidH4fVBLtiBGE8w0ko8aa3zLB4iu/fdATuEZ1r+gPt40SWo1mKahoCgY3
 BOtYxEZTnd4l4GPJDQIaYgVckgSiex0xcE9pRJ5LtBwdyw9g4jwfb39WHx4MT2sE
 u5xWDbadU/ZM6ppLJ/iBkotvU9D7ohKTviN1IfCwjqL7IMBNz5c+nBwLQZWK4YTR
 2ySKpv1VOMROglt1KvnHfdpmdJ+CjAJLLNE+XSz9AHXinK5v8aM=
 =bUN1
 -----END PGP SIGNATURE-----

Merge tag '9p-for-6.18-rc1' of https://github.com/martinetd/linux

Pull 9p updates from Dominique Martinet:
 "A bunch of unrelated fixes:

   - polling fix for trans fd that ought to have been fixed otherwise
     back in March, but apparently came back somewhere else...

   - USB transport buffer overflow fix

   - Some dentry lifetime rework to handle metadata update for currently
     opened files in uncached mode, or inode type change in cached mode

   - a double-put on invalid flush found by syzbot

   - and finally /sys/fs/9p/caches not advancing buffer and overwriting
     itself for large contents

  Thanks to everyone involved!"

* tag '9p-for-6.18-rc1' of https://github.com/martinetd/linux:
  9p: sysfs_init: don't hardcode error to ENOMEM
  9p: fix /sys/fs/9p/caches overwriting itself
  9p: clean up comment typos
  9p/trans_fd: p9_fd_request: kick rx thread if EPOLLIN
  net/9p: fix double req put in p9_fd_cancelled
  net/9p: Fix buffer overflow in USB transport layer
  fs/9p: Add p9_debug(VFS) in d_revalidate
  fs/9p: Invalidate dentry if inode type change detected in cached mode
  fs/9p: Refresh metadata in d_revalidate for uncached mode too
This commit is contained in:
Linus Torvalds 2025-10-09 11:56:59 -07:00
commit 80b7065ec1
6 changed files with 70 additions and 28 deletions

View File

@ -438,8 +438,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
v9ses->flags &= ~V9FS_ACCESS_MASK; v9ses->flags &= ~V9FS_ACCESS_MASK;
v9ses->flags |= V9FS_ACCESS_USER; v9ses->flags |= V9FS_ACCESS_USER;
} }
/*FIXME !! */ /* FIXME: for legacy mode, fall back to V9FS_ACCESS_ANY */
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) && if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) { ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
@ -450,7 +449,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
if (!v9fs_proto_dotl(v9ses) || if (!v9fs_proto_dotl(v9ses) ||
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
/* /*
* We support ACL checks on clinet only if the protocol is * We support ACL checks on client only if the protocol is
* 9P2000.L and access is V9FS_ACCESS_CLIENT. * 9P2000.L and access is V9FS_ACCESS_CLIENT.
*/ */
v9ses->flags &= ~V9FS_ACL_MASK; v9ses->flags &= ~V9FS_ACL_MASK;
@ -561,7 +560,7 @@ static ssize_t caches_show(struct kobject *kobj,
spin_lock(&v9fs_sessionlist_lock); spin_lock(&v9fs_sessionlist_lock);
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) { list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
if (v9ses->cachetag) { if (v9ses->cachetag) {
n = snprintf(buf, limit, "%s\n", v9ses->cachetag); n = snprintf(buf + count, limit, "%s\n", v9ses->cachetag);
if (n < 0) { if (n < 0) {
count = n; count = n;
break; break;
@ -597,13 +596,16 @@ static const struct attribute_group v9fs_attr_group = {
static int __init v9fs_sysfs_init(void) static int __init v9fs_sysfs_init(void)
{ {
int ret;
v9fs_kobj = kobject_create_and_add("9p", fs_kobj); v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
if (!v9fs_kobj) if (!v9fs_kobj)
return -ENOMEM; return -ENOMEM;
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) { ret = sysfs_create_group(v9fs_kobj, &v9fs_attr_group);
if (ret) {
kobject_put(v9fs_kobj); kobject_put(v9fs_kobj);
return -ENOMEM; return ret;
} }
return 0; return 0;
@ -669,7 +671,7 @@ static int __init init_v9fs(void)
int err; int err;
pr_info("Installing v9fs 9p2000 file system support\n"); pr_info("Installing v9fs 9p2000 file system support\n");
/* TODO: Setup list of registered trasnport modules */ /* TODO: Setup list of registered transport modules */
err = v9fs_init_inode_cache(); err = v9fs_init_inode_cache();
if (err < 0) { if (err < 0) {

View File

@ -66,6 +66,7 @@ static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
struct p9_fid *fid; struct p9_fid *fid;
struct inode *inode; struct inode *inode;
struct v9fs_inode *v9inode; struct v9fs_inode *v9inode;
unsigned int cached;
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
@ -75,13 +76,22 @@ static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
goto out_valid; goto out_valid;
v9inode = V9FS_I(inode); v9inode = V9FS_I(inode);
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
cached = v9ses->cache & (CACHE_META | CACHE_LOOSE);
if (!cached || v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
int retval; int retval;
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
fid = v9fs_fid_lookup(dentry); fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid)) if (IS_ERR(fid)) {
p9_debug(
P9_DEBUG_VFS,
"v9fs_fid_lookup: dentry = %pd (%p), got error %pe\n",
dentry, dentry, fid);
return PTR_ERR(fid); return PTR_ERR(fid);
}
v9ses = v9fs_inode2v9ses(inode); v9ses = v9fs_inode2v9ses(inode);
if (v9fs_proto_dotl(v9ses)) if (v9fs_proto_dotl(v9ses))
@ -90,12 +100,25 @@ static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
retval = v9fs_refresh_inode(fid, inode); retval = v9fs_refresh_inode(fid, inode);
p9_fid_put(fid); p9_fid_put(fid);
if (retval == -ENOENT) if (retval == -ENOENT) {
p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) invalidated due to ENOENT\n",
dentry, dentry);
return 0; return 0;
if (retval < 0) }
if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) {
p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) invalidated due to type change\n",
dentry, dentry);
return 0;
}
if (retval < 0) {
p9_debug(P9_DEBUG_VFS,
"refresh inode: dentry = %pd (%p), got error %pe\n",
dentry, dentry, ERR_PTR(retval));
return retval; return retval;
} }
}
out_valid: out_valid:
p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) is valid\n", dentry, dentry);
return 1; return 1;
} }
@ -127,6 +150,8 @@ const struct dentry_operations v9fs_cached_dentry_operations = {
}; };
const struct dentry_operations v9fs_dentry_operations = { const struct dentry_operations v9fs_dentry_operations = {
.d_revalidate = v9fs_lookup_revalidate,
.d_weak_revalidate = __v9fs_lookup_revalidate,
.d_release = v9fs_dentry_release, .d_release = v9fs_dentry_release,
.d_unalias_trylock = v9fs_dentry_unalias_trylock, .d_unalias_trylock = v9fs_dentry_unalias_trylock,
.d_unalias_unlock = v9fs_dentry_unalias_unlock, .d_unalias_unlock = v9fs_dentry_unalias_unlock,

View File

@ -1339,8 +1339,14 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
* Don't update inode if the file type is different * Don't update inode if the file type is different
*/ */
umode = p9mode2unixmode(v9ses, st, &rdev); umode = p9mode2unixmode(v9ses, st, &rdev);
if (inode_wrong_type(inode, umode)) if (inode_wrong_type(inode, umode)) {
/*
* Do this as a way of letting the caller know the inode should not
* be reused
*/
v9fs_invalidate_inode_attr(inode);
goto out; goto out;
}
/* /*
* We don't want to refresh inode->i_size, * We don't want to refresh inode->i_size,

View File

@ -897,8 +897,14 @@ int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
/* /*
* Don't update inode if the file type is different * Don't update inode if the file type is different
*/ */
if (inode_wrong_type(inode, st->st_mode)) if (inode_wrong_type(inode, st->st_mode)) {
/*
* Do this as a way of letting the caller know the inode should not
* be reused
*/
v9fs_invalidate_inode_attr(inode);
goto out; goto out;
}
/* /*
* We don't want to refresh inode->i_size, * We don't want to refresh inode->i_size,

View File

@ -666,7 +666,6 @@ static void p9_poll_mux(struct p9_conn *m)
static int p9_fd_request(struct p9_client *client, struct p9_req_t *req) static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
{ {
__poll_t n;
int err; int err;
struct p9_trans_fd *ts = client->trans; struct p9_trans_fd *ts = client->trans;
struct p9_conn *m = &ts->conn; struct p9_conn *m = &ts->conn;
@ -686,13 +685,7 @@ static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
list_add_tail(&req->req_list, &m->unsent_req_list); list_add_tail(&req->req_list, &m->unsent_req_list);
spin_unlock(&m->req_lock); spin_unlock(&m->req_lock);
if (test_and_clear_bit(Wpending, &m->wsched)) p9_poll_mux(m);
n = EPOLLOUT;
else
n = p9_fd_poll(m->client, NULL, NULL);
if (n & EPOLLOUT && !test_and_set_bit(Wworksched, &m->wsched))
schedule_work(&m->wq);
return 0; return 0;
} }
@ -726,10 +719,10 @@ static int p9_fd_cancelled(struct p9_client *client, struct p9_req_t *req)
p9_debug(P9_DEBUG_TRANS, "client %p req %p\n", client, req); p9_debug(P9_DEBUG_TRANS, "client %p req %p\n", client, req);
spin_lock(&m->req_lock); spin_lock(&m->req_lock);
/* Ignore cancelled request if message has been received /* Ignore cancelled request if status changed since the request was
* before lock. * processed in p9_client_flush()
*/ */
if (req->status == REQ_STATUS_RCVD) { if (req->status != REQ_STATUS_SENT) {
spin_unlock(&m->req_lock); spin_unlock(&m->req_lock);
return 0; return 0;
} }

View File

@ -231,6 +231,8 @@ static void usb9pfs_rx_complete(struct usb_ep *ep, struct usb_request *req)
struct f_usb9pfs *usb9pfs = ep->driver_data; struct f_usb9pfs *usb9pfs = ep->driver_data;
struct usb_composite_dev *cdev = usb9pfs->function.config->cdev; struct usb_composite_dev *cdev = usb9pfs->function.config->cdev;
struct p9_req_t *p9_rx_req; struct p9_req_t *p9_rx_req;
unsigned int req_size = req->actual;
int status = REQ_STATUS_RCVD;
if (req->status) { if (req->status) {
dev_err(&cdev->gadget->dev, "%s usb9pfs complete --> %d, %d/%d\n", dev_err(&cdev->gadget->dev, "%s usb9pfs complete --> %d, %d/%d\n",
@ -242,11 +244,19 @@ static void usb9pfs_rx_complete(struct usb_ep *ep, struct usb_request *req)
if (!p9_rx_req) if (!p9_rx_req)
return; return;
memcpy(p9_rx_req->rc.sdata, req->buf, req->actual); if (req_size > p9_rx_req->rc.capacity) {
dev_err(&cdev->gadget->dev,
"%s received data size %u exceeds buffer capacity %zu\n",
ep->name, req_size, p9_rx_req->rc.capacity);
req_size = 0;
status = REQ_STATUS_ERROR;
}
p9_rx_req->rc.size = req->actual; memcpy(p9_rx_req->rc.sdata, req->buf, req_size);
p9_client_cb(usb9pfs->client, p9_rx_req, REQ_STATUS_RCVD); p9_rx_req->rc.size = req_size;
p9_client_cb(usb9pfs->client, p9_rx_req, status);
p9_req_put(usb9pfs->client, p9_rx_req); p9_req_put(usb9pfs->client, p9_rx_req);
complete(&usb9pfs->received); complete(&usb9pfs->received);