• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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