1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright 2018 Google LLC
4 */
5
6 /*
7 * Regression test for commit f43f39958beb ("crypto: user - fix leaking
8 * uninitialized memory to userspace"), or CVE-2018-19854; it was also a
9 * re-introduction of CVE-2013-2547. This bug caused uninitialized kernel stack
10 * memory to be leaked in some string fields in the replies to CRYPTO_MSG_GETALG
11 * messages over NETLINK_CRYPTO. To try to detect the bug, this test dumps all
12 * algorithms using NLM_F_DUMP mode and checks all string fields for unexpected
13 * nonzero bytes.
14 */
15
16 #include <stdlib.h>
17
18 #include "tst_test.h"
19 #include "tst_crypto.h"
20 #include "tst_netlink.h"
21
22 /*
23 * include after <sys/socket.h> (via tst_test.h), to work around dependency bug
24 * in old kernel headers (https://www.spinics.net/lists/netdev/msg171764.html)
25 */
26 #include <linux/rtnetlink.h>
27
28 static struct tst_crypto_session ses = TST_CRYPTO_SESSION_INIT;
29
setup(void)30 static void setup(void)
31 {
32 tst_crypto_open(&ses);
33 }
34
do_check_for_leaks(const char * name,const char * value,size_t vlen)35 static void do_check_for_leaks(const char *name, const char *value, size_t vlen)
36 {
37 size_t i;
38
39 for (i = strnlen(value, vlen); i < vlen; i++) {
40 if (value[i] != '\0')
41 tst_brk(TFAIL, "information leak in field '%s'", name);
42 }
43 }
44
45 #define check_for_leaks(name, field) \
46 do_check_for_leaks(name, field, sizeof(field))
47
validate_attr(const struct rtattr * rta)48 static void validate_attr(const struct rtattr *rta)
49 {
50 switch (rta->rta_type) {
51 case CRYPTOCFGA_REPORT_LARVAL: {
52 const struct crypto_report_larval *p = RTA_DATA(rta);
53
54 check_for_leaks("crypto_report_larval::type", p->type);
55 break;
56 }
57 case CRYPTOCFGA_REPORT_HASH: {
58 const struct crypto_report_hash *p = RTA_DATA(rta);
59
60 check_for_leaks("crypto_report_hash::type", p->type);
61 break;
62 }
63 case CRYPTOCFGA_REPORT_BLKCIPHER: {
64 const struct crypto_report_blkcipher *p = RTA_DATA(rta);
65
66 check_for_leaks("crypto_report_blkcipher::type", p->type);
67 check_for_leaks("crypto_report_blkcipher::geniv", p->geniv);
68 break;
69 }
70 case CRYPTOCFGA_REPORT_AEAD: {
71 const struct crypto_report_aead *p = RTA_DATA(rta);
72
73 check_for_leaks("crypto_report_aead::type", p->type);
74 check_for_leaks("crypto_report_aead::geniv", p->geniv);
75 break;
76 }
77 case CRYPTOCFGA_REPORT_COMPRESS: {
78 const struct crypto_report_comp *p = RTA_DATA(rta);
79
80 check_for_leaks("crypto_report_comp::type", p->type);
81 break;
82 }
83 case CRYPTOCFGA_REPORT_RNG: {
84 const struct crypto_report_rng *p = RTA_DATA(rta);
85
86 check_for_leaks("crypto_report_rng::type", p->type);
87 break;
88 }
89 case CRYPTOCFGA_REPORT_CIPHER: {
90 const struct crypto_report_cipher *p = RTA_DATA(rta);
91
92 check_for_leaks("crypto_report_cipher::type", p->type);
93 break;
94 }
95 case CRYPTOCFGA_REPORT_AKCIPHER: {
96 const struct crypto_report_akcipher *p = RTA_DATA(rta);
97
98 check_for_leaks("crypto_report_akcipher::type", p->type);
99 break;
100 }
101 case CRYPTOCFGA_REPORT_KPP: {
102 const struct crypto_report_kpp *p = RTA_DATA(rta);
103
104 check_for_leaks("crypto_report_kpp::type", p->type);
105 break;
106 }
107 case CRYPTOCFGA_REPORT_ACOMP: {
108 const struct crypto_report_acomp *p = RTA_DATA(rta);
109
110 check_for_leaks("crypto_report_acomp::type", p->type);
111 break;
112 }
113 } /* end switch */
114 }
115
validate_one_alg(const struct nlmsghdr * nh)116 static void validate_one_alg(const struct nlmsghdr *nh)
117 {
118 const struct crypto_user_alg *alg = NLMSG_DATA(nh);
119 const struct rtattr *rta = (void *)alg + NLMSG_ALIGN(sizeof(*alg));
120 size_t remaining = NLMSG_PAYLOAD(nh, sizeof(*alg));
121
122 check_for_leaks("crypto_user_alg::cru_name", alg->cru_name);
123 check_for_leaks("crypto_user_alg::cru_driver_name",
124 alg->cru_driver_name);
125 check_for_leaks("crypto_user_alg::cru_module_name",
126 alg->cru_module_name);
127
128 while (RTA_OK(rta, remaining)) {
129 validate_attr(rta);
130 rta = RTA_NEXT(rta, remaining);
131 }
132 }
133
validate_alg_list(const void * buf,size_t remaining)134 static void validate_alg_list(const void *buf, size_t remaining)
135 {
136 const struct nlmsghdr *nh;
137
138 for (nh = buf; NLMSG_OK(nh, remaining);
139 nh = NLMSG_NEXT(nh, remaining)) {
140 if (nh->nlmsg_seq != ses.seq_num) {
141 tst_brk(TBROK,
142 "Message out of sequence; type=0%hx, seq_num=%u (not %u)",
143 nh->nlmsg_type, nh->nlmsg_seq, ses.seq_num);
144 }
145 if (nh->nlmsg_type == NLMSG_DONE)
146 return;
147 if (nh->nlmsg_type != CRYPTO_MSG_GETALG) {
148 tst_brk(TBROK,
149 "Unexpected message type; type=0x%hx, seq_num=%u",
150 nh->nlmsg_type, nh->nlmsg_seq);
151 }
152 validate_one_alg(nh);
153 }
154 }
155
run(void)156 static void run(void)
157 {
158 struct crypto_user_alg payload = { 0 };
159 struct nlmsghdr nh = {
160 .nlmsg_len = sizeof(payload),
161 .nlmsg_type = CRYPTO_MSG_GETALG,
162 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
163 .nlmsg_seq = ++(ses.seq_num),
164 .nlmsg_pid = 0,
165 };
166 /*
167 * Due to an apparent kernel bug, this API cannot be used incrementally,
168 * so we just use a large recvmsg() buffer. This is good enough since
169 * we don't necessarily have to check every algorithm for this test to
170 * be effective...
171 */
172 const size_t bufsize = 1048576;
173 void *buf = SAFE_MALLOC(bufsize);
174 size_t res;
175
176 SAFE_NETLINK_SEND(ses.fd, &nh, &payload);
177
178 res = SAFE_NETLINK_RECV(ses.fd, buf, bufsize);
179
180 validate_alg_list(buf, res);
181
182 free(buf);
183 tst_res(TPASS, "No information leaks found");
184 }
185
cleanup(void)186 static void cleanup(void)
187 {
188 tst_crypto_close(&ses);
189 }
190
191 static struct tst_test test = {
192 .setup = setup,
193 .test_all = run,
194 .cleanup = cleanup,
195 .tags = (const struct tst_tag[]) {
196 {"linux-git", "f43f39958beb"},
197 {"CVE", "2013-2547"},
198 {"CVE", "2018-19854"},
199 {}
200 }
201 };
202