]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests: tls: add tls record_size_limit test
authorWilfred Mallawa <wilfred.mallawa@wdc.com>
Wed, 22 Oct 2025 00:19:37 +0000 (10:19 +1000)
committerJakub Kicinski <kuba@kernel.org>
Mon, 27 Oct 2025 23:13:43 +0000 (16:13 -0700)
Test that outgoing plaintext records respect the tls TLS_TX_MAX_PAYLOAD_LEN
set using setsockopt(). The limit is set to be 128, thus, in all received
records, the plaintext must not exceed this amount.

Also test that setting a new record size limit whilst a pending open
record exists is handled correctly by discarding the request.

Suggested-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/20251022001937.20155-2-wilfred.opensource@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/testing/selftests/net/tls.c

index 5c6d8215021c9c7f337984918fd1e244786c3617..da1b50b30719460ba01fe0b0795d28ee949fcd6c 100644 (file)
@@ -2856,6 +2856,147 @@ TEST_F(tls_err, oob_pressure)
                EXPECT_EQ(send(self->fd2, buf, 5, MSG_OOB), 5);
 }
 
+/*
+ * Parse a stream of TLS records and ensure that each record respects
+ * the specified @max_payload_len.
+ */
+static size_t parse_tls_records(struct __test_metadata *_metadata,
+                               const __u8 *rx_buf, int rx_len, int overhead,
+                               __u16 max_payload_len)
+{
+       const __u8 *rec = rx_buf;
+       size_t total_plaintext_rx = 0;
+       const __u8 rec_header_len = 5;
+
+       while (rec < rx_buf + rx_len) {
+               __u16 record_payload_len;
+               __u16 plaintext_len;
+
+               /* Sanity check that it's a TLS header for application data */
+               ASSERT_EQ(rec[0], 23);
+               ASSERT_EQ(rec[1], 0x3);
+               ASSERT_EQ(rec[2], 0x3);
+
+               memcpy(&record_payload_len, rec + 3, 2);
+               record_payload_len = ntohs(record_payload_len);
+               ASSERT_GE(record_payload_len, overhead);
+
+               plaintext_len = record_payload_len - overhead;
+               total_plaintext_rx += plaintext_len;
+
+               /* Plaintext must not exceed the specified limit */
+               ASSERT_LE(plaintext_len, max_payload_len);
+               rec += rec_header_len + record_payload_len;
+       }
+
+       return total_plaintext_rx;
+}
+
+TEST(tls_12_tx_max_payload_len)
+{
+       struct tls_crypto_info_keys tls12;
+       int cfd, ret, fd, overhead;
+       size_t total_plaintext_rx = 0;
+       __u8 tx[1024], rx[2000];
+       __u16 limit = 128;
+       __u16 opt = 0;
+       unsigned int optlen = sizeof(opt);
+       bool notls;
+
+       tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_CCM_128,
+                            &tls12, 0);
+
+       ulp_sock_pair(_metadata, &fd, &cfd, &notls);
+
+       if (notls)
+               exit(KSFT_SKIP);
+
+       /* Don't install keys on fd, we'll parse raw records */
+       ret = setsockopt(cfd, SOL_TLS, TLS_TX, &tls12, tls12.len);
+       ASSERT_EQ(ret, 0);
+
+       ret = setsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &limit,
+                        sizeof(limit));
+       ASSERT_EQ(ret, 0);
+
+       ret = getsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &opt, &optlen);
+       EXPECT_EQ(ret, 0);
+       EXPECT_EQ(limit, opt);
+       EXPECT_EQ(optlen, sizeof(limit));
+
+       memset(tx, 0, sizeof(tx));
+       ASSERT_EQ(send(cfd, tx, sizeof(tx), 0), sizeof(tx));
+       close(cfd);
+
+       ret = recv(fd, rx, sizeof(rx), 0);
+
+       /*
+        * 16B tag + 8B IV -- record header (5B) is not counted but we'll
+        * need it to walk the record stream
+        */
+       overhead = 16 + 8;
+       total_plaintext_rx = parse_tls_records(_metadata, rx, ret, overhead,
+                                              limit);
+
+       ASSERT_EQ(total_plaintext_rx, sizeof(tx));
+       close(fd);
+}
+
+TEST(tls_12_tx_max_payload_len_open_rec)
+{
+       struct tls_crypto_info_keys tls12;
+       int cfd, ret, fd, overhead;
+       size_t total_plaintext_rx = 0;
+       __u8 tx[1024], rx[2000];
+       __u16 tx_partial = 256;
+       __u16 og_limit = 512, limit = 128;
+       bool notls;
+
+       tls_crypto_info_init(TLS_1_2_VERSION, TLS_CIPHER_AES_CCM_128,
+                            &tls12, 0);
+
+       ulp_sock_pair(_metadata, &fd, &cfd, &notls);
+
+       if (notls)
+               exit(KSFT_SKIP);
+
+       /* Don't install keys on fd, we'll parse raw records */
+       ret = setsockopt(cfd, SOL_TLS, TLS_TX, &tls12, tls12.len);
+       ASSERT_EQ(ret, 0);
+
+       ret = setsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &og_limit,
+                        sizeof(og_limit));
+       ASSERT_EQ(ret, 0);
+
+       memset(tx, 0, sizeof(tx));
+       ASSERT_EQ(send(cfd, tx, tx_partial, MSG_MORE), tx_partial);
+
+       /*
+        * Changing the payload limit with a pending open record should
+        * not be allowed.
+        */
+       ret = setsockopt(cfd, SOL_TLS, TLS_TX_MAX_PAYLOAD_LEN, &limit,
+                        sizeof(limit));
+       ASSERT_EQ(ret, -1);
+       ASSERT_EQ(errno, EBUSY);
+
+       ASSERT_EQ(send(cfd, tx + tx_partial, sizeof(tx) - tx_partial, MSG_EOR),
+                 sizeof(tx) - tx_partial);
+       close(cfd);
+
+       ret = recv(fd, rx, sizeof(rx), 0);
+
+       /*
+        * 16B tag + 8B IV -- record header (5B) is not counted but we'll
+        * need it to walk the record stream
+        */
+       overhead = 16 + 8;
+       total_plaintext_rx = parse_tls_records(_metadata, rx, ret, overhead,
+                                              og_limit);
+       ASSERT_EQ(total_plaintext_rx, sizeof(tx));
+       close(fd);
+}
+
 TEST(non_established) {
        struct tls12_crypto_info_aes_gcm_256 tls12;
        struct sockaddr_in addr;