vfs-6.17-rc8.fixes

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaNZQKAAKCRCRxhvAZXjc
 ol/SAQDWweiwhUQ6XCP34oeUEbwEZfNFe+l9Igr0Xxjv7JLtEwEA8z7YydWbvWI4
 GamGbXSanr30orQXnHq0JpDapRWxVw4=
 =AimW
 -----END PGP SIGNATURE-----

Merge tag 'vfs-6.17-rc8.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs fixes from Christian Brauner:

 - Prevent double unlock in netfs

 - Fix a NULL pointer dereference in afs_put_server()

 - Fix a reference leak in netfs

* tag 'vfs-6.17-rc8.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  netfs: fix reference leak
  afs: Fix potential null pointer dereference in afs_put_server
  netfs: Prevent duplicate unlocking
This commit is contained in:
Linus Torvalds 2025-09-26 10:57:25 -07:00
commit d8743676b1
10 changed files with 50 additions and 16 deletions

View File

@ -331,13 +331,14 @@ struct afs_server *afs_use_server(struct afs_server *server, bool activate,
void afs_put_server(struct afs_net *net, struct afs_server *server,
enum afs_server_trace reason)
{
unsigned int a, debug_id = server->debug_id;
unsigned int a, debug_id;
bool zero;
int r;
if (!server)
return;
debug_id = server->debug_id;
a = atomic_read(&server->active);
zero = __refcount_dec_and_test(&server->ref, &r);
trace_afs_server(debug_id, r - 1, a, reason);

View File

@ -369,7 +369,7 @@ void netfs_readahead(struct readahead_control *ractl)
return netfs_put_request(rreq, netfs_rreq_trace_put_return);
cleanup_free:
return netfs_put_request(rreq, netfs_rreq_trace_put_failed);
return netfs_put_failed_request(rreq);
}
EXPORT_SYMBOL(netfs_readahead);
@ -472,7 +472,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)
return ret < 0 ? ret : 0;
discard:
netfs_put_request(rreq, netfs_rreq_trace_put_discard);
netfs_put_failed_request(rreq);
alloc_error:
folio_unlock(folio);
return ret;
@ -532,7 +532,7 @@ int netfs_read_folio(struct file *file, struct folio *folio)
return ret < 0 ? ret : 0;
discard:
netfs_put_request(rreq, netfs_rreq_trace_put_discard);
netfs_put_failed_request(rreq);
alloc_error:
folio_unlock(folio);
return ret;
@ -699,7 +699,7 @@ int netfs_write_begin(struct netfs_inode *ctx,
return 0;
error_put:
netfs_put_request(rreq, netfs_rreq_trace_put_failed);
netfs_put_failed_request(rreq);
error:
if (folio) {
folio_unlock(folio);
@ -754,7 +754,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
return ret < 0 ? ret : 0;
error_put:
netfs_put_request(rreq, netfs_rreq_trace_put_discard);
netfs_put_failed_request(rreq);
error:
_leave(" = %d", ret);
return ret;

View File

@ -347,7 +347,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
folio_put(folio);
ret = filemap_write_and_wait_range(mapping, fpos, fpos + flen - 1);
if (ret < 0)
goto error_folio_unlock;
goto out;
continue;
copied:

View File

@ -131,6 +131,7 @@ static ssize_t netfs_unbuffered_read(struct netfs_io_request *rreq, bool sync)
if (rreq->len == 0) {
pr_err("Zero-sized read [R=%x]\n", rreq->debug_id);
netfs_put_request(rreq, netfs_rreq_trace_put_discard);
return -EIO;
}
@ -205,7 +206,7 @@ ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *i
if (user_backed_iter(iter)) {
ret = netfs_extract_user_iter(iter, rreq->len, &rreq->buffer.iter, 0);
if (ret < 0)
goto out;
goto error_put;
rreq->direct_bv = (struct bio_vec *)rreq->buffer.iter.bvec;
rreq->direct_bv_count = ret;
rreq->direct_bv_unpin = iov_iter_extract_will_pin(iter);
@ -238,6 +239,10 @@ ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *i
if (ret > 0)
orig_count -= ret;
return ret;
error_put:
netfs_put_failed_request(rreq);
return ret;
}
EXPORT_SYMBOL(netfs_unbuffered_read_iter_locked);

View File

@ -57,7 +57,7 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *
n = netfs_extract_user_iter(iter, len, &wreq->buffer.iter, 0);
if (n < 0) {
ret = n;
goto out;
goto error_put;
}
wreq->direct_bv = (struct bio_vec *)wreq->buffer.iter.bvec;
wreq->direct_bv_count = n;
@ -101,6 +101,10 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *
out:
netfs_put_request(wreq, netfs_rreq_trace_put_return);
return ret;
error_put:
netfs_put_failed_request(wreq);
return ret;
}
EXPORT_SYMBOL(netfs_unbuffered_write_iter_locked);

View File

@ -87,6 +87,7 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
void netfs_get_request(struct netfs_io_request *rreq, enum netfs_rreq_ref_trace what);
void netfs_clear_subrequests(struct netfs_io_request *rreq);
void netfs_put_request(struct netfs_io_request *rreq, enum netfs_rreq_ref_trace what);
void netfs_put_failed_request(struct netfs_io_request *rreq);
struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq);
static inline void netfs_see_request(struct netfs_io_request *rreq,

View File

@ -116,10 +116,8 @@ static void netfs_free_request_rcu(struct rcu_head *rcu)
netfs_stat_d(&netfs_n_rh_rreq);
}
static void netfs_free_request(struct work_struct *work)
static void netfs_deinit_request(struct netfs_io_request *rreq)
{
struct netfs_io_request *rreq =
container_of(work, struct netfs_io_request, cleanup_work);
struct netfs_inode *ictx = netfs_inode(rreq->inode);
unsigned int i;
@ -149,6 +147,14 @@ static void netfs_free_request(struct work_struct *work)
if (atomic_dec_and_test(&ictx->io_count))
wake_up_var(&ictx->io_count);
}
static void netfs_free_request(struct work_struct *work)
{
struct netfs_io_request *rreq =
container_of(work, struct netfs_io_request, cleanup_work);
netfs_deinit_request(rreq);
call_rcu(&rreq->rcu, netfs_free_request_rcu);
}
@ -167,6 +173,24 @@ void netfs_put_request(struct netfs_io_request *rreq, enum netfs_rreq_ref_trace
}
}
/*
* Free a request (synchronously) that was just allocated but has
* failed before it could be submitted.
*/
void netfs_put_failed_request(struct netfs_io_request *rreq)
{
int r = refcount_read(&rreq->ref);
/* new requests have two references (see
* netfs_alloc_request(), and this function is only allowed on
* new request objects
*/
WARN_ON_ONCE(r != 2);
trace_netfs_rreq_ref(rreq->debug_id, r, netfs_rreq_trace_put_failed);
netfs_free_request(&rreq->cleanup_work);
}
/*
* Allocate and partially initialise an I/O request structure.
*/

View File

@ -118,7 +118,7 @@ static struct netfs_io_request *netfs_pgpriv2_begin_copy_to_cache(
return creq;
cancel_put:
netfs_put_request(creq, netfs_rreq_trace_put_return);
netfs_put_failed_request(creq);
cancel:
rreq->copy_to_cache = ERR_PTR(-ENOBUFS);
clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags);

View File

@ -189,7 +189,7 @@ ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_ite
return ret;
cleanup_free:
netfs_put_request(rreq, netfs_rreq_trace_put_failed);
netfs_put_failed_request(rreq);
return ret;
}
EXPORT_SYMBOL(netfs_read_single);

View File

@ -133,8 +133,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
return wreq;
nomem:
wreq->error = -ENOMEM;
netfs_put_request(wreq, netfs_rreq_trace_put_failed);
netfs_put_failed_request(wreq);
return ERR_PTR(-ENOMEM);
}