Skip to content

Commit

Permalink
Merge tag '9p-for-6.15-rc1' of https://github.com/martinetd/linux
Browse files Browse the repository at this point in the history
Pull 9p updates from Dominique Martinet:

 - fix handling of bogus (negative/too long) replies

 - fix crash on mkdir with ACLs (... looks like nobody is using ACLs
   with semi-recent kernels...)

 - ipv6 support for trans=tcp

 - minor concurrency fix to make syzbot happy

 - minor cleanup

* tag '9p-for-6.15-rc1' of https://github.com/martinetd/linux:
  docs: fs/9p: Add missing "not" in cache documentation
  9p: Use hashtable.h for hash_errmap
  Documentation/fs/9p: fix broken link
  9p/trans_fd: mark concurrent read and writes to p9_conn->err
  9p/net: return error on bogus (longer than requested) replies
  9p/net: fix improper handling of bogus negative read/write replies
  fs/9p: fix NULL pointer dereference on mkdir
  net/9p/fd: support ipv6 for trans=tcp
  • Loading branch information
Linus Torvalds committed Apr 3, 2025
2 parents 5916a6f + 4210030 commit bdafff6
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 73 deletions.
6 changes: 3 additions & 3 deletions Documentation/filesystems/9p.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ For remote file server::

mount -t 9p 10.10.1.2 /mnt/9

For Plan 9 From User Space applications (http://swtch.com/plan9)::
For Plan 9 From User Space applications (https://9fans.github.io/plan9port/)::

mount -t 9p `namespace`/acme /mnt/9 -o trans=unix,uname=$USER

Expand Down Expand Up @@ -165,8 +165,8 @@ Options
do not necessarily validate cached values on the server. In other
words changes on the server are not guaranteed to be reflected
on the client system. Only use this mode of operation if you
have an exclusive mount and the server will modify the filesystem
underneath you.
have an exclusive mount and the server will not modify the
filesystem underneath you.

debug=n specifies debug level. The debug level is a bitmask.

Expand Down
2 changes: 1 addition & 1 deletion fs/9p/vfs_inode_dotl.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,8 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap,
err);
goto error;
}
v9fs_fid_add(dentry, &fid);
v9fs_set_create_acl(inode, fid, dacl, pacl);
v9fs_fid_add(dentry, &fid);
d_instantiate(dentry, inode);
err = 0;
inc_nlink(dir);
Expand Down
44 changes: 26 additions & 18 deletions net/9p/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1548,7 +1548,8 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to,
struct p9_client *clnt = fid->clnt;
struct p9_req_t *req;
int count = iov_iter_count(to);
int rsize, received, non_zc = 0;
u32 rsize, received;
bool non_zc = false;
char *dataptr;

*err = 0;
Expand All @@ -1571,7 +1572,7 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to,
0, 11, "dqd", fid->fid,
offset, rsize);
} else {
non_zc = 1;
non_zc = true;
req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset,
rsize);
}
Expand All @@ -1592,11 +1593,13 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to,
return 0;
}
if (rsize < received) {
pr_err("bogus RREAD count (%d > %d)\n", received, rsize);
received = rsize;
pr_err("bogus RREAD count (%u > %u)\n", received, rsize);
*err = -EIO;
p9_req_put(clnt, req);
return 0;
}

p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", received);
p9_debug(P9_DEBUG_9P, "<<< RREAD count %u\n", received);

if (non_zc) {
int n = copy_to_iter(dataptr, received, to);
Expand All @@ -1623,17 +1626,17 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
*err = 0;

while (iov_iter_count(from)) {
int count = iov_iter_count(from);
int rsize = fid->iounit;
int written;
size_t count = iov_iter_count(from);
u32 rsize = fid->iounit;
u32 written;

if (!rsize || rsize > clnt->msize - P9_IOHDRSZ)
rsize = clnt->msize - P9_IOHDRSZ;

if (count < rsize)
rsize = count;

p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d (/%d)\n",
p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %u (/%zu)\n",
fid->fid, offset, rsize, count);

/* Don't bother zerocopy for small IO (< 1024) */
Expand All @@ -1659,11 +1662,14 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
break;
}
if (rsize < written) {
pr_err("bogus RWRITE count (%d > %d)\n", written, rsize);
written = rsize;
pr_err("bogus RWRITE count (%u > %u)\n", written, rsize);
*err = -EIO;
iov_iter_revert(from, count - iov_iter_count(from));
p9_req_put(clnt, req);
break;
}

p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", written);
p9_debug(P9_DEBUG_9P, "<<< RWRITE count %u\n", written);

p9_req_put(clnt, req);
iov_iter_revert(from, count - written - iov_iter_count(from));
Expand Down Expand Up @@ -1712,7 +1718,7 @@ p9_client_write_subreq(struct netfs_io_subrequest *subreq)

if (written > len) {
pr_err("bogus RWRITE count (%d > %u)\n", written, len);
written = len;
written = -EIO;
}

p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", len);
Expand Down Expand Up @@ -2098,7 +2104,8 @@ EXPORT_SYMBOL_GPL(p9_client_xattrcreate);

int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
{
int err, rsize, non_zc = 0;
int err, non_zc = 0;
u32 rsize;
struct p9_client *clnt;
struct p9_req_t *req;
char *dataptr;
Expand All @@ -2107,7 +2114,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)

iov_iter_kvec(&to, ITER_DEST, &kv, 1, count);

p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n",
p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %u\n",
fid->fid, offset, count);

clnt = fid->clnt;
Expand Down Expand Up @@ -2142,11 +2149,12 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
goto free_and_error;
}
if (rsize < count) {
pr_err("bogus RREADDIR count (%d > %d)\n", count, rsize);
count = rsize;
pr_err("bogus RREADDIR count (%u > %u)\n", count, rsize);
err = -EIO;
goto free_and_error;
}

p9_debug(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count);
p9_debug(P9_DEBUG_9P, "<<< RREADDIR count %u\n", count);

if (non_zc)
memmove(data, dataptr, count);
Expand Down
21 changes: 9 additions & 12 deletions net/9p/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/jhash.h>
#include <linux/errno.h>
#include <linux/hashtable.h>
#include <net/9p/9p.h>

/**
Expand All @@ -33,8 +34,8 @@ struct errormap {
struct hlist_node list;
};

#define ERRHASHSZ 32
static struct hlist_head hash_errmap[ERRHASHSZ];
#define ERRHASH_BITS 5
static DEFINE_HASHTABLE(hash_errmap, ERRHASH_BITS);

/* FixMe - reduce to a reasonable size */
static struct errormap errmap[] = {
Expand Down Expand Up @@ -176,18 +177,14 @@ static struct errormap errmap[] = {
int p9_error_init(void)
{
struct errormap *c;
int bucket;

/* initialize hash table */
for (bucket = 0; bucket < ERRHASHSZ; bucket++)
INIT_HLIST_HEAD(&hash_errmap[bucket]);
u32 hash;

/* load initial error map into hash table */
for (c = errmap; c->name; c++) {
c->namelen = strlen(c->name);
bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ;
hash = jhash(c->name, c->namelen, 0);
INIT_HLIST_NODE(&c->list);
hlist_add_head(&c->list, &hash_errmap[bucket]);
hash_add(hash_errmap, &c->list, hash);
}

return 1;
Expand All @@ -205,12 +202,12 @@ int p9_errstr2errno(char *errstr, int len)
{
int errno;
struct errormap *c;
int bucket;
u32 hash;

errno = 0;
c = NULL;
bucket = jhash(errstr, len, 0) % ERRHASHSZ;
hlist_for_each_entry(c, &hash_errmap[bucket], list) {
hash = jhash(errstr, len, 0);
hash_for_each_possible(hash_errmap, c, list, hash) {
if (c->namelen == len && !memcmp(c->name, errstr, len)) {
errno = c->val;
break;
Expand Down
73 changes: 34 additions & 39 deletions net/9p/trans_fd.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/in.h>
#include <linux/in6.h>
#include <linux/module.h>
#include <linux/net.h>
#include <linux/ipv6.h>
Expand Down Expand Up @@ -191,12 +192,13 @@ static void p9_conn_cancel(struct p9_conn *m, int err)

spin_lock(&m->req_lock);

if (m->err) {
if (READ_ONCE(m->err)) {
spin_unlock(&m->req_lock);
return;
}

m->err = err;
WRITE_ONCE(m->err, err);
ASSERT_EXCLUSIVE_WRITER(m->err);

list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
list_move(&req->req_list, &cancel_list);
Expand Down Expand Up @@ -283,7 +285,7 @@ static void p9_read_work(struct work_struct *work)

m = container_of(work, struct p9_conn, rq);

if (m->err < 0)
if (READ_ONCE(m->err) < 0)
return;

p9_debug(P9_DEBUG_TRANS, "start mux %p pos %zd\n", m, m->rc.offset);
Expand Down Expand Up @@ -450,7 +452,7 @@ static void p9_write_work(struct work_struct *work)

m = container_of(work, struct p9_conn, wq);

if (m->err < 0) {
if (READ_ONCE(m->err) < 0) {
clear_bit(Wworksched, &m->wsched);
return;
}
Expand Down Expand Up @@ -622,7 +624,7 @@ static void p9_poll_mux(struct p9_conn *m)
__poll_t n;
int err = -ECONNRESET;

if (m->err < 0)
if (READ_ONCE(m->err) < 0)
return;

n = p9_fd_poll(m->client, NULL, &err);
Expand Down Expand Up @@ -665,6 +667,7 @@ static void p9_poll_mux(struct p9_conn *m)
static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
{
__poll_t n;
int err;
struct p9_trans_fd *ts = client->trans;
struct p9_conn *m = &ts->conn;

Expand All @@ -673,9 +676,10 @@ static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)

spin_lock(&m->req_lock);

if (m->err < 0) {
err = READ_ONCE(m->err);
if (err < 0) {
spin_unlock(&m->req_lock);
return m->err;
return err;
}

WRITE_ONCE(req->status, REQ_STATUS_UNSENT);
Expand Down Expand Up @@ -954,64 +958,55 @@ static void p9_fd_close(struct p9_client *client)
kfree(ts);
}

/*
* stolen from NFS - maybe should be made a generic function?
*/
static inline int valid_ipaddr4(const char *buf)
{
int rc, count, in[4];

rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
if (rc != 4)
return -EINVAL;
for (count = 0; count < 4; count++) {
if (in[count] > 255)
return -EINVAL;
}
return 0;
}

static int p9_bind_privport(struct socket *sock)
{
struct sockaddr_in cl;
struct sockaddr_storage stor = { 0 };
int port, err = -EINVAL;

memset(&cl, 0, sizeof(cl));
cl.sin_family = AF_INET;
cl.sin_addr.s_addr = htonl(INADDR_ANY);
stor.ss_family = sock->ops->family;
if (stor.ss_family == AF_INET)
((struct sockaddr_in *)&stor)->sin_addr.s_addr = htonl(INADDR_ANY);
else
((struct sockaddr_in6 *)&stor)->sin6_addr = in6addr_any;
for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) {
cl.sin_port = htons((ushort)port);
err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl));
if (stor.ss_family == AF_INET)
((struct sockaddr_in *)&stor)->sin_port = htons((ushort)port);
else
((struct sockaddr_in6 *)&stor)->sin6_port = htons((ushort)port);
err = kernel_bind(sock, (struct sockaddr *)&stor, sizeof(stor));
if (err != -EADDRINUSE)
break;
}
return err;
}


static int
p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
{
int err;
char port_str[6];
struct socket *csocket;
struct sockaddr_in sin_server;
struct sockaddr_storage stor = { 0 };
struct p9_fd_opts opts;

err = parse_opts(args, &opts);
if (err < 0)
return err;

if (addr == NULL || valid_ipaddr4(addr) < 0)
if (!addr)
return -EINVAL;

sprintf(port_str, "%u", opts.port);
err = inet_pton_with_scope(current->nsproxy->net_ns, AF_UNSPEC, addr,
port_str, &stor);
if (err < 0)
return err;

csocket = NULL;

client->trans_opts.tcp.port = opts.port;
client->trans_opts.tcp.privport = opts.privport;
sin_server.sin_family = AF_INET;
sin_server.sin_addr.s_addr = in_aton(addr);
sin_server.sin_port = htons(opts.port);
err = __sock_create(current->nsproxy->net_ns, PF_INET,
err = __sock_create(current->nsproxy->net_ns, stor.ss_family,
SOCK_STREAM, IPPROTO_TCP, &csocket, 1);
if (err) {
pr_err("%s (%d): problem creating socket\n",
Expand All @@ -1030,8 +1025,8 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
}

err = READ_ONCE(csocket->ops)->connect(csocket,
(struct sockaddr *)&sin_server,
sizeof(struct sockaddr_in), 0);
(struct sockaddr *)&stor,
sizeof(stor), 0);
if (err < 0) {
pr_err("%s (%d): problem connecting socket to %s\n",
__func__, task_pid_nr(current), addr);
Expand Down

0 comments on commit bdafff6

Please sign in to comment.