]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/ceph/messenger.c
UBUNTU: Ubuntu-4.15.0-96.97
[mirror_ubuntu-bionic-kernel.git] / net / ceph / messenger.c
index 8a4d3758030b73d3b9ca24b09f91b1e65be0844e..ff432512f433a5f3ee2fa6418360f4325a8dac52 100644 (file)
@@ -595,9 +595,15 @@ static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
        struct bio_vec bvec;
        int ret;
 
-       /* sendpage cannot properly handle pages with page_count == 0,
-        * we need to fallback to sendmsg if that's the case */
-       if (page_count(page) >= 1)
+       /*
+        * sendpage cannot properly handle pages with page_count == 0,
+        * we need to fall back to sendmsg if that's the case.
+        *
+        * Same goes for slab pages: skb_can_coalesce() allows
+        * coalescing neighboring slab objects into a single frag which
+        * triggers one of hardened usercopy checks.
+        */
+       if (page_count(page) >= 1 && !PageSlab(page))
                return __ceph_tcp_sendpage(sock, page, offset, size, more);
 
        bvec.bv_page = page;
@@ -1406,24 +1412,26 @@ static void prepare_write_keepalive(struct ceph_connection *con)
  * Connection negotiation.
  */
 
-static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection *con,
-                                               int *auth_proto)
+static int get_connect_authorizer(struct ceph_connection *con)
 {
        struct ceph_auth_handshake *auth;
+       int auth_proto;
 
        if (!con->ops->get_authorizer) {
+               con->auth = NULL;
                con->out_connect.authorizer_protocol = CEPH_AUTH_UNKNOWN;
                con->out_connect.authorizer_len = 0;
-               return NULL;
+               return 0;
        }
 
-       auth = con->ops->get_authorizer(con, auth_proto, con->auth_retry);
+       auth = con->ops->get_authorizer(con, &auth_proto, con->auth_retry);
        if (IS_ERR(auth))
-               return auth;
+               return PTR_ERR(auth);
 
-       con->auth_reply_buf = auth->authorizer_reply_buf;
-       con->auth_reply_buf_len = auth->authorizer_reply_buf_len;
-       return auth;
+       con->auth = auth;
+       con->out_connect.authorizer_protocol = cpu_to_le32(auth_proto);
+       con->out_connect.authorizer_len = cpu_to_le32(auth->authorizer_buf_len);
+       return 0;
 }
 
 /*
@@ -1439,12 +1447,22 @@ static void prepare_write_banner(struct ceph_connection *con)
        con_flag_set(con, CON_FLAG_WRITE_PENDING);
 }
 
+static void __prepare_write_connect(struct ceph_connection *con)
+{
+       con_out_kvec_add(con, sizeof(con->out_connect), &con->out_connect);
+       if (con->auth)
+               con_out_kvec_add(con, con->auth->authorizer_buf_len,
+                                con->auth->authorizer_buf);
+
+       con->out_more = 0;
+       con_flag_set(con, CON_FLAG_WRITE_PENDING);
+}
+
 static int prepare_write_connect(struct ceph_connection *con)
 {
        unsigned int global_seq = get_global_seq(con->msgr, 0);
        int proto;
-       int auth_proto;
-       struct ceph_auth_handshake *auth;
+       int ret;
 
        switch (con->peer_name.type) {
        case CEPH_ENTITY_TYPE_MON:
@@ -1471,24 +1489,11 @@ static int prepare_write_connect(struct ceph_connection *con)
        con->out_connect.protocol_version = cpu_to_le32(proto);
        con->out_connect.flags = 0;
 
-       auth_proto = CEPH_AUTH_UNKNOWN;
-       auth = get_connect_authorizer(con, &auth_proto);
-       if (IS_ERR(auth))
-               return PTR_ERR(auth);
-
-       con->out_connect.authorizer_protocol = cpu_to_le32(auth_proto);
-       con->out_connect.authorizer_len = auth ?
-               cpu_to_le32(auth->authorizer_buf_len) : 0;
-
-       con_out_kvec_add(con, sizeof (con->out_connect),
-                                       &con->out_connect);
-       if (auth && auth->authorizer_buf_len)
-               con_out_kvec_add(con, auth->authorizer_buf_len,
-                                       auth->authorizer_buf);
-
-       con->out_more = 0;
-       con_flag_set(con, CON_FLAG_WRITE_PENDING);
+       ret = get_connect_authorizer(con);
+       if (ret)
+               return ret;
 
+       __prepare_write_connect(con);
        return 0;
 }
 
@@ -1748,11 +1753,21 @@ static int read_partial_connect(struct ceph_connection *con)
        if (ret <= 0)
                goto out;
 
-       size = le32_to_cpu(con->in_reply.authorizer_len);
-       end += size;
-       ret = read_partial(con, end, size, con->auth_reply_buf);
-       if (ret <= 0)
-               goto out;
+       if (con->auth) {
+               size = le32_to_cpu(con->in_reply.authorizer_len);
+               if (size > con->auth->authorizer_reply_buf_len) {
+                       pr_err("authorizer reply too big: %d > %zu\n", size,
+                              con->auth->authorizer_reply_buf_len);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               end += size;
+               ret = read_partial(con, end, size,
+                                  con->auth->authorizer_reply_buf);
+               if (ret <= 0)
+                       goto out;
+       }
 
        dout("read_partial_connect %p tag %d, con_seq = %u, g_seq = %u\n",
             con, (int)con->in_reply.tag,
@@ -1760,7 +1775,6 @@ static int read_partial_connect(struct ceph_connection *con)
             le32_to_cpu(con->in_reply.global_seq));
 out:
        return ret;
-
 }
 
 /*
@@ -2043,16 +2057,34 @@ static int process_connect(struct ceph_connection *con)
 
        dout("process_connect on %p tag %d\n", con, (int)con->in_tag);
 
-       if (con->auth_reply_buf) {
+       if (con->auth) {
+               int len = le32_to_cpu(con->in_reply.authorizer_len);
+
                /*
                 * Any connection that defines ->get_authorizer()
-                * should also define ->verify_authorizer_reply().
+                * should also define ->add_authorizer_challenge() and
+                * ->verify_authorizer_reply().
+                *
                 * See get_connect_authorizer().
                 */
-               ret = con->ops->verify_authorizer_reply(con);
-               if (ret < 0) {
-                       con->error_msg = "bad authorize reply";
-                       return ret;
+               if (con->in_reply.tag == CEPH_MSGR_TAG_CHALLENGE_AUTHORIZER) {
+                       ret = con->ops->add_authorizer_challenge(
+                                   con, con->auth->authorizer_reply_buf, len);
+                       if (ret < 0)
+                               return ret;
+
+                       con_out_kvec_reset(con);
+                       __prepare_write_connect(con);
+                       prepare_read_connect(con);
+                       return 0;
+               }
+
+               if (len) {
+                       ret = con->ops->verify_authorizer_reply(con);
+                       if (ret < 0) {
+                               con->error_msg = "bad authorize reply";
+                               return ret;
+                       }
                }
        }
 
@@ -2531,6 +2563,11 @@ static int try_write(struct ceph_connection *con)
        int ret = 1;
 
        dout("try_write start %p state %lu\n", con, con->state);
+       if (con->state != CON_STATE_PREOPEN &&
+           con->state != CON_STATE_CONNECTING &&
+           con->state != CON_STATE_NEGOTIATING &&
+           con->state != CON_STATE_OPEN)
+               return 0;
 
 more:
        dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes);
@@ -2556,6 +2593,8 @@ more:
        }
 
 more_kvec:
+       BUG_ON(!con->sock);
+
        /* kvec data queued? */
        if (con->out_kvec_left) {
                ret = write_partial_kvec(con);
@@ -3175,9 +3214,10 @@ void ceph_con_keepalive(struct ceph_connection *con)
        dout("con_keepalive %p\n", con);
        mutex_lock(&con->mutex);
        clear_standby(con);
+       con_flag_set(con, CON_FLAG_KEEPALIVE_PENDING);
        mutex_unlock(&con->mutex);
-       if (con_flag_test_and_set(con, CON_FLAG_KEEPALIVE_PENDING) == 0 &&
-           con_flag_test_and_set(con, CON_FLAG_WRITE_PENDING) == 0)
+
+       if (con_flag_test_and_set(con, CON_FLAG_WRITE_PENDING) == 0)
                queue_con(con);
 }
 EXPORT_SYMBOL(ceph_con_keepalive);