Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

X.509: Introduce scope-based x509_certificate allocation

Add a DEFINE_FREE() clause for x509_certificate structs and use it in
x509_cert_parse() and x509_key_preparse(). These are the only functions
where scope-based x509_certificate allocation currently makes sense.
A third user will be introduced with the forthcoming SPDM library
(Security Protocol and Data Model) for PCI device authentication.

Unlike most other DEFINE_FREE() clauses, this one checks for IS_ERR()
instead of NULL before calling x509_free_certificate() at end of scope.
That's because the "constructor" of x509_certificate structs,
x509_cert_parse(), returns a valid pointer or an ERR_PTR(), but never
NULL.

Comparing the Assembler output before/after has shown they are identical,
save for the fact that gcc-12 always generates two return paths when
__cleanup() is used, one for the success case and one for the error case.

In x509_cert_parse(), add a hint for the compiler that kzalloc() never
returns an ERR_PTR(). Otherwise the compiler adds a gratuitous IS_ERR()
check on return. Introduce an assume() macro for this which can be
re-used elsewhere in the kernel to provide hints for the compiler.

Suggested-by: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
Link: https://lore.kernel.org/all/20231003153937.000034ca@Huawei.com/
Link: https://lwn.net/Articles/934679/
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Lukas Wunner and committed by
Herbert Xu
5c6ca9d9 c9ccfd5e

+30 -49
+16 -27
crypto/asymmetric_keys/x509_cert_parser.c
··· 60 60 */ 61 61 struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) 62 62 { 63 - struct x509_certificate *cert; 64 - struct x509_parse_context *ctx; 63 + struct x509_certificate *cert __free(x509_free_certificate); 64 + struct x509_parse_context *ctx __free(kfree) = NULL; 65 65 struct asymmetric_key_id *kid; 66 66 long ret; 67 67 68 - ret = -ENOMEM; 69 68 cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL); 69 + assume(!IS_ERR(cert)); /* Avoid gratuitous IS_ERR() check on return */ 70 70 if (!cert) 71 - goto error_no_cert; 71 + return ERR_PTR(-ENOMEM); 72 72 cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); 73 73 if (!cert->pub) 74 - goto error_no_ctx; 74 + return ERR_PTR(-ENOMEM); 75 75 cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL); 76 76 if (!cert->sig) 77 - goto error_no_ctx; 77 + return ERR_PTR(-ENOMEM); 78 78 ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); 79 79 if (!ctx) 80 - goto error_no_ctx; 80 + return ERR_PTR(-ENOMEM); 81 81 82 82 ctx->cert = cert; 83 83 ctx->data = (unsigned long)data; ··· 85 85 /* Attempt to decode the certificate */ 86 86 ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen); 87 87 if (ret < 0) 88 - goto error_decode; 88 + return ERR_PTR(ret); 89 89 90 90 /* Decode the AuthorityKeyIdentifier */ 91 91 if (ctx->raw_akid) { ··· 95 95 ctx->raw_akid, ctx->raw_akid_size); 96 96 if (ret < 0) { 97 97 pr_warn("Couldn't decode AuthKeyIdentifier\n"); 98 - goto error_decode; 98 + return ERR_PTR(ret); 99 99 } 100 100 } 101 101 102 - ret = -ENOMEM; 103 102 cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL); 104 103 if (!cert->pub->key) 105 - goto error_decode; 104 + return ERR_PTR(-ENOMEM); 106 105 107 106 cert->pub->keylen = ctx->key_size; 108 107 109 108 cert->pub->params = kmemdup(ctx->params, ctx->params_size, GFP_KERNEL); 110 109 if (!cert->pub->params) 111 - goto error_decode; 110 + return ERR_PTR(-ENOMEM); 112 111 113 112 cert->pub->paramlen = ctx->params_size; 114 113 cert->pub->algo = ctx->key_algo; ··· 115 116 /* Grab the signature bits */ 116 117 ret = x509_get_sig_params(cert); 117 118 if (ret < 0) 118 - goto error_decode; 119 + return ERR_PTR(ret); 119 120 120 121 /* Generate cert issuer + serial number key ID */ 121 122 kid = asymmetric_key_generate_id(cert->raw_serial, 122 123 cert->raw_serial_size, 123 124 cert->raw_issuer, 124 125 cert->raw_issuer_size); 125 - if (IS_ERR(kid)) { 126 - ret = PTR_ERR(kid); 127 - goto error_decode; 128 - } 126 + if (IS_ERR(kid)) 127 + return ERR_CAST(kid); 129 128 cert->id = kid; 130 129 131 130 /* Detect self-signed certificates */ 132 131 ret = x509_check_for_self_signed(cert); 133 132 if (ret < 0) 134 - goto error_decode; 133 + return ERR_PTR(ret); 135 134 136 - kfree(ctx); 137 - return cert; 138 - 139 - error_decode: 140 - kfree(ctx); 141 - error_no_ctx: 142 - x509_free_certificate(cert); 143 - error_no_cert: 144 - return ERR_PTR(ret); 135 + return_ptr(cert); 145 136 } 146 137 EXPORT_SYMBOL_GPL(x509_cert_parse); 147 138
+3
crypto/asymmetric_keys/x509_parser.h
··· 5 5 * Written by David Howells (dhowells@redhat.com) 6 6 */ 7 7 8 + #include <linux/cleanup.h> 8 9 #include <linux/time.h> 9 10 #include <crypto/public_key.h> 10 11 #include <keys/asymmetric-type.h> ··· 45 44 * x509_cert_parser.c 46 45 */ 47 46 extern void x509_free_certificate(struct x509_certificate *cert); 47 + DEFINE_FREE(x509_free_certificate, struct x509_certificate *, 48 + if (!IS_ERR(_T)) x509_free_certificate(_T)) 48 49 extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); 49 50 extern int x509_decode_time(time64_t *_t, size_t hdrlen, 50 51 unsigned char tag,
+9 -22
crypto/asymmetric_keys/x509_public_key.c
··· 161 161 */ 162 162 static int x509_key_preparse(struct key_preparsed_payload *prep) 163 163 { 164 - struct asymmetric_key_ids *kids; 165 - struct x509_certificate *cert; 164 + struct x509_certificate *cert __free(x509_free_certificate); 165 + struct asymmetric_key_ids *kids __free(kfree) = NULL; 166 + char *p, *desc __free(kfree) = NULL; 166 167 const char *q; 167 168 size_t srlen, sulen; 168 - char *desc = NULL, *p; 169 - int ret; 170 169 171 170 cert = x509_cert_parse(prep->data, prep->datalen); 172 171 if (IS_ERR(cert)) ··· 187 188 } 188 189 189 190 /* Don't permit addition of blacklisted keys */ 190 - ret = -EKEYREJECTED; 191 191 if (cert->blacklisted) 192 - goto error_free_cert; 192 + return -EKEYREJECTED; 193 193 194 194 /* Propose a description */ 195 195 sulen = strlen(cert->subject); ··· 200 202 q = cert->raw_serial; 201 203 } 202 204 203 - ret = -ENOMEM; 204 205 desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL); 205 206 if (!desc) 206 - goto error_free_cert; 207 + return -ENOMEM; 207 208 p = memcpy(desc, cert->subject, sulen); 208 209 p += sulen; 209 210 *p++ = ':'; ··· 212 215 213 216 kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL); 214 217 if (!kids) 215 - goto error_free_desc; 218 + return -ENOMEM; 216 219 kids->id[0] = cert->id; 217 220 kids->id[1] = cert->skid; 218 221 kids->id[2] = asymmetric_key_generate_id(cert->raw_subject, 219 222 cert->raw_subject_size, 220 223 "", 0); 221 - if (IS_ERR(kids->id[2])) { 222 - ret = PTR_ERR(kids->id[2]); 223 - goto error_free_kids; 224 - } 224 + if (IS_ERR(kids->id[2])) 225 + return PTR_ERR(kids->id[2]); 225 226 226 227 /* We're pinning the module by being linked against it */ 227 228 __module_get(public_key_subtype.owner); ··· 237 242 cert->sig = NULL; 238 243 desc = NULL; 239 244 kids = NULL; 240 - ret = 0; 241 - 242 - error_free_kids: 243 - kfree(kids); 244 - error_free_desc: 245 - kfree(desc); 246 - error_free_cert: 247 - x509_free_certificate(cert); 248 - return ret; 245 + return 0; 249 246 } 250 247 251 248 static struct asymmetric_key_parser x509_key_parser = {
+2
include/linux/compiler.h
··· 148 148 } while (0) 149 149 #endif 150 150 151 + #define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) 152 + 151 153 /* 152 154 * KENTRY - kernel entry point 153 155 * This can be used to annotate symbols (functions or data) that are used