Re: [PATCH RFC] libceph: replace WARN_ON() with error handling for network input
Aleksandr Nogikh <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <CANp29Y67g9oxPXTVPfCowAT2sLq0SCrNMm_BvJGotUHgiN8nxA@mail.gmail.com> |
On Sat, Aug 8, 2026 at 11:53 AM 'syzbot' via syzkaller-upstream-moderation <[email protected]> wrote: > > The Ceph messenger v1 and v2 protocol implementations currently use > WARN_ON() to assert that the server's response adheres to the protocol. > However, network input from a server can be invalid due to a buggy or > malicious server. WARN_ON() must not be used for conditions that can > legitimately happen, such as receiving invalid network data. While WARNINGs > are not fatal by default, they pollute the kernel log with stack traces and > can lead to kernel panics on systems configured with panic_on_warn. > > When the kernel Ceph client connects to a Ceph server, it initiates a > handshake and reads the server's reply directly from the network socket. > The reply is then processed in process_connect() (for v1) or other > processing functions (for v2). If the server sends an unexpected value, > such as an incorrect connect_seq, the client triggers a WARN_ON(), > resulting in the following warning: > > ------------[ cut here ]------------ > con->v1.connect_seq != le32_to_cpu(con->v1.in_reply.connect_seq) > WARNING: net/ceph/messenger_v1.c:902 at process_connect > net/ceph/messenger_v1.c:901 [inline], CPU#1: kworker/1:3/5645 > WARNING: net/ceph/messenger_v1.c:902 at ceph_con_v1_try_read+0x3335/0x6eb0 > net/ceph/messenger_v1.c:1366, CPU#1: kworker/1:3/5645 > RIP: 0010:process_connect net/ceph/messenger_v1.c:901 [inline] > RIP: 0010:ceph_con_v1_try_read+0x3335/0x6eb0 net/ceph/messenger_v1.c:1366 > Call Trace: > <TASK> > ceph_con_workfn+0x208/0x14a0 net/ceph/messenger.c:1577 > process_one_work kernel/workqueue.c:3322 [inline] > process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 > worker_thread+0xa47/0xfb0 kernel/workqueue.c:3486 > kthread+0x388/0x470 kernel/kthread.c:436 > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > </TASK> > > Fix this by replacing WARN_ON() assertions on network input with graceful > error handling. If the server sends unexpected data, log a descriptive > error message using pr_err() instead, set the connection error message, and > return an error code to abort the connection. This ensures that invalid > network input is treated as a protocol violation rather than a kernel bug. > The fix is applied to both messenger v1 (process_connect) and v2 > (process_hello, process_server_ident, process_session_retry, and > process_session_retry_global). > > Fixes: 31b8006e1d79 ("ceph: messenger library") > Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot > Reported-by: [email protected] > Closes: https://syzkaller.appspot.com/bug?extid=21f8628595b2061c438d > Link: https://syzkaller.appspot.com/ai_job?id=56e865ad-bcea-4dc1-9df8-618a118f27c9 > To: "Alex Markuze" <[email protected]> > To: <[email protected]> > To: "Ilya Dryomov" <[email protected]> > To: "Viacheslav Dubeyko" <[email protected]> > To: "Sage Weil" <[email protected]> > Cc: <[email protected]> > > --- > diff --git a/net/ceph/messenger_v1.c b/net/ceph/messenger_v1.c > index c9e002d96..df2248b7b 100644 > --- a/net/ceph/messenger_v1.c > +++ b/net/ceph/messenger_v1.c > @@ -898,8 +898,15 @@ static int process_connect(struct ceph_connection *con) > con->v1.peer_global_seq, > le32_to_cpu(con->v1.in_reply.connect_seq), > con->v1.connect_seq); > - WARN_ON(con->v1.connect_seq != > - le32_to_cpu(con->v1.in_reply.connect_seq)); > + if (con->v1.connect_seq != le32_to_cpu(con->v1.in_reply.connect_seq)) { > + pr_err("%s%lld %s connect_seq mismatch, expected %u, got %u\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr), > + con->v1.connect_seq, > + le32_to_cpu(con->v1.in_reply.connect_seq)); > + con->error_msg = "connect_seq mismatch"; > + return -1; > + } > > if (con->v1.in_reply.flags & CEPH_MSG_CONNECT_LOSSY) > ceph_con_flag_set(con, CEPH_CON_F_LOSSYTX); > diff --git a/net/ceph/messenger_v2.c b/net/ceph/messenger_v2.c > index 05f6eea29..966a5f800 100644 > --- a/net/ceph/messenger_v2.c > +++ b/net/ceph/messenger_v2.c > @@ -2237,9 +2237,16 @@ static int process_hello(struct ceph_connection *con, void *p, void *end) > __func__, con, ceph_pr_addr(my_addr)); > } > > - WARN_ON(ceph_addr_is_blank(my_addr) || ceph_addr_port(my_addr)); > - WARN_ON(my_addr->type != CEPH_ENTITY_ADDR_TYPE_ANY); > - WARN_ON(!my_addr->nonce); > + if (ceph_addr_is_blank(my_addr) || ceph_addr_port(my_addr) || > + my_addr->type != CEPH_ENTITY_ADDR_TYPE_ANY || > + !my_addr->nonce) { Don't we actually want to leave my_addr validation as it was - it's not coming from the remote device? > + pr_err("%s%lld %s bad my_addr %s\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr), > + ceph_pr_addr(my_addr)); > + con->error_msg = "bad my_addr"; > + return -EINVAL; > + } > > /* no reset_out_kvecs() as our hello may still be pending */ > ret = prepare_auth_request(con); > @@ -2529,14 +2536,33 @@ static int process_server_ident(struct ceph_connection *con, > con->peer_name.num = cpu_to_le64(global_id); > con->v2.peer_global_seq = global_seq; > con->peer_features = features; > - WARN_ON(required_features & ~client->supported_features); > + if (required_features & ~client->supported_features) { > + pr_err("%s%lld %s missing required protocol features, my supported 0x%llx, server's required 0x%llx\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr), > + client->supported_features, required_features); > + con->error_msg = "missing required protocol features"; > + return -EINVAL; > + } > con->v2.server_cookie = cookie; > > if (flags & CEPH_MSG_CONNECT_LOSSY) { > ceph_con_flag_set(con, CEPH_CON_F_LOSSYTX); > - WARN_ON(con->v2.server_cookie); > + if (con->v2.server_cookie) { > + pr_err("%s%lld %s server_cookie is set for lossy connection\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr)); > + con->error_msg = "server_cookie is set for lossy connection"; > + return -EINVAL; > + } > } else { > - WARN_ON(!con->v2.server_cookie); > + if (!con->v2.server_cookie) { > + pr_err("%s%lld %s server_cookie is not set for lossless connection\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr)); > + con->error_msg = "server_cookie is not set for lossless connection"; > + return -EINVAL; > + } > } > > clear_in_sign_kvecs(con); > @@ -2618,7 +2644,14 @@ static int process_session_retry(struct ceph_connection *con, > ceph_decode_64_safe(&p, end, connect_seq, bad); > > dout("%s con %p connect_seq %llu\n", __func__, con, connect_seq); > - WARN_ON(connect_seq <= con->v2.connect_seq); > + if (connect_seq <= con->v2.connect_seq) { > + pr_err("%s%lld %s connect_seq mismatch, expected > %llu, got %llu\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr), > + con->v2.connect_seq, connect_seq); > + con->error_msg = "connect_seq mismatch"; > + return -EINVAL; > + } > con->v2.connect_seq = connect_seq + 1; > > free_conn_bufs(con); > @@ -2651,7 +2684,14 @@ static int process_session_retry_global(struct ceph_connection *con, > ceph_decode_64_safe(&p, end, global_seq, bad); > > dout("%s con %p global_seq %llu\n", __func__, con, global_seq); > - WARN_ON(global_seq <= con->v2.global_seq); > + if (global_seq <= con->v2.global_seq) { > + pr_err("%s%lld %s global_seq mismatch, expected > %llu, got %llu\n", > + ENTITY_NAME(con->peer_name), > + ceph_pr_addr(&con->peer_addr), > + con->v2.global_seq, global_seq); > + con->error_msg = "global_seq mismatch"; > + return -EINVAL; > + } > con->v2.global_seq = ceph_get_global_seq(con->msgr, global_seq); > > free_conn_bufs(con); > > > base-commit: 075b74841bd0065a3bda3440873c747938e69b68 > --