mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
Merge tag 'v7.3-p1' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
Pull crypto update from Herbert Xu: "API: - Add af_alg_restrict sysctl and white list - Fix potential suspend/resume races in hwrng Algorithms: - Optimize vli additive operations using compiler builtins in ecc Drivers: - Remove unsafe/deprecated algorithms from qce - Mark qce as BROKEN - Add runtime PM and interconnect bandwidth scaling support to qce - Remove crypto_rng from qcom, sun8i and caam - Fix SG list issues in iaa - Fix SEV init path bugs in ccp" * tag 'v7.3-p1' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6: (122 commits) crypto: lskcipher - propagate errors from unaligned crypt crypto: keembay - use crypto_memneq() to compare CCM AEAD tags crypto: keembay - use crypto_memneq() to compare GCM AEAD tags crypto: sa2ul - use crypto_memneq() to compare AEAD tag hwrng: drivers - use named initializers for acpi_device_id crypto: qce - fix CCM AAD buffer underallocation crypto: iaa - unmap dst before software fallback on decompress crypto: iaa - use bounce buffer for multi-sg decompress input crypto: iaa - avoid counting fallback decompression bytes crypto: iaa - fall back to software for multi-entry scatterlists hwrng: core - Stop/start hwrng_fillfn() kthread before/after suspend-resume crypto: hisilicon/sec2 - fix CCM algorithm long packet failure crypto: eip93 - use struct_size() and flexible array for ring allocation crypto: krb5 - use kfree_sensitive() for derived key buffers crypto: af_alg - Stop after finding name in allowlist crypto: af_alg - Replace 'bool privileged' with flags crypto: af_alg - Make cbc(paes) privileged-only hwrng: imx-rngc - Disable clock on registration failure crypto: qat - remove dead ADF_HEX code crypto: qce - simplify qce_handle_request ...
This commit is contained in:
+70
-6
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/capability.h>
|
||||
#include <crypto/if_alg.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/init.h>
|
||||
@@ -22,10 +23,28 @@
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
|
||||
static int af_alg_restrict = 1;
|
||||
|
||||
static const struct ctl_table af_alg_table[] = {
|
||||
{
|
||||
.procname = "af_alg_restrict",
|
||||
.data = &af_alg_restrict,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_TWO,
|
||||
},
|
||||
};
|
||||
|
||||
static struct ctl_table_header *af_alg_header;
|
||||
|
||||
struct alg_type_list {
|
||||
const struct af_alg_type *type;
|
||||
struct list_head list;
|
||||
@@ -110,6 +129,43 @@ int af_alg_unregister_type(const struct af_alg_type *type)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(af_alg_unregister_type);
|
||||
|
||||
static bool af_alg_capable(void)
|
||||
{
|
||||
return ns_capable_noaudit(&init_user_ns, CAP_NET_ADMIN) ||
|
||||
capable(CAP_SYS_ADMIN);
|
||||
}
|
||||
|
||||
int af_alg_check_restriction(const char *name,
|
||||
const struct af_alg_allowlist_entry allowlist[])
|
||||
{
|
||||
int level = READ_ONCE(af_alg_restrict);
|
||||
|
||||
if (level == 0)
|
||||
return 0;
|
||||
if (level == 1) {
|
||||
for (const struct af_alg_allowlist_entry *ent = allowlist;
|
||||
ent->name; ent++) {
|
||||
if (strcmp(name, ent->name) == 0) {
|
||||
if ((ent->flags & AF_ALG_UNPRIVILEGED) ||
|
||||
af_alg_capable())
|
||||
return 0;
|
||||
/* List contains at most one entry per name. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Use -ENOENT (the error code for "algorithm not found") instead of
|
||||
* -EACCES or -EPERM, for the highest chance of correctly triggering
|
||||
* fallback code paths in userspace programs.
|
||||
*
|
||||
* Don't log a warning, since it would be noisy. iwd tries to bind a
|
||||
* bunch of algorithms that it never uses.
|
||||
*/
|
||||
return -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(af_alg_check_restriction);
|
||||
|
||||
static void alg_do_release(const struct af_alg_type *type, void *private)
|
||||
{
|
||||
if (!type)
|
||||
@@ -506,6 +562,9 @@ static int alg_create(struct net *net, struct socket *sock, int protocol,
|
||||
struct sock *sk;
|
||||
int err;
|
||||
|
||||
if (READ_ONCE(af_alg_restrict) == 2)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (sock->type != SOCK_SEQPACKET)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
if (protocol != 0)
|
||||
@@ -1222,27 +1281,32 @@ EXPORT_SYMBOL_GPL(af_alg_get_rsgl);
|
||||
|
||||
static int __init af_alg_init(void)
|
||||
{
|
||||
int err = proto_register(&alg_proto, 0);
|
||||
int err;
|
||||
|
||||
af_alg_header = register_sysctl("crypto", af_alg_table);
|
||||
|
||||
err = proto_register(&alg_proto, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
goto out_unregister_sysctl;
|
||||
|
||||
err = sock_register(&alg_family);
|
||||
if (err != 0)
|
||||
if (err)
|
||||
goto out_unregister_proto;
|
||||
|
||||
out:
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
out_unregister_proto:
|
||||
proto_unregister(&alg_proto);
|
||||
goto out;
|
||||
out_unregister_sysctl:
|
||||
unregister_sysctl_table(af_alg_header);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit af_alg_exit(void)
|
||||
{
|
||||
sock_unregister(PF_ALG);
|
||||
proto_unregister(&alg_proto);
|
||||
unregister_sysctl_table(af_alg_header);
|
||||
}
|
||||
|
||||
module_init(af_alg_init);
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
#include <linux/net.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
static const struct af_alg_allowlist_entry aead_allowlist[] = {
|
||||
{ "ccm(aes)" }, /* bluez */
|
||||
{},
|
||||
};
|
||||
|
||||
static inline bool aead_sufficient_data(struct sock *sk)
|
||||
{
|
||||
struct alg_sock *ask = alg_sk(sk);
|
||||
@@ -344,6 +349,12 @@ static struct proto_ops algif_aead_ops_nokey = {
|
||||
|
||||
static void *aead_bind(const char *name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = af_alg_check_restriction(name, aead_allowlist);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return crypto_alloc_aead(name, 0, AF_ALG_CRYPTOAPI_MASK);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,24 @@
|
||||
#include <linux/net.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
static const struct af_alg_allowlist_entry hash_allowlist[] = {
|
||||
{ "cmac(aes)" }, /* iwd, bluez */
|
||||
{ "hmac(md5)" }, /* iwd */
|
||||
{ "hmac(sha1)" }, /* iwd */
|
||||
{ "hmac(sha224)" }, /* iwd */
|
||||
{ "hmac(sha256)" }, /* iwd */
|
||||
{ "hmac(sha384)" }, /* iwd */
|
||||
{ "hmac(sha512)" }, /* iwd, sha512hmac */
|
||||
{ "md4" }, /* iwd */
|
||||
{ "md5" }, /* iwd */
|
||||
{ "sha1", AF_ALG_UNPRIVILEGED }, /* iwd, iproute2 < 7.0 */
|
||||
{ "sha224" }, /* iwd */
|
||||
{ "sha256" }, /* iwd */
|
||||
{ "sha384" }, /* iwd */
|
||||
{ "sha512" }, /* iwd */
|
||||
{},
|
||||
};
|
||||
|
||||
struct hash_ctx {
|
||||
struct af_alg_sgl sgl;
|
||||
|
||||
@@ -382,6 +400,12 @@ static struct proto_ops algif_hash_ops_nokey = {
|
||||
|
||||
static void *hash_bind(const char *name)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = af_alg_check_restriction(name, hash_allowlist);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return crypto_alloc_ahash(name, 0, AF_ALG_CRYPTOAPI_MASK);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
|
||||
MODULE_DESCRIPTION("User-space interface for random number generators");
|
||||
|
||||
static const struct af_alg_allowlist_entry rng_allowlist[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
struct rng_ctx {
|
||||
#define MAXSIZE 128
|
||||
unsigned int len;
|
||||
@@ -201,6 +205,11 @@ static void *rng_bind(const char *name)
|
||||
{
|
||||
struct rng_parent_ctx *pctx;
|
||||
struct crypto_rng *rng;
|
||||
int err;
|
||||
|
||||
err = af_alg_check_restriction(name, rng_allowlist);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
pctx = kzalloc_obj(*pctx);
|
||||
if (!pctx)
|
||||
|
||||
@@ -35,6 +35,24 @@
|
||||
#include <linux/string.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
static const struct af_alg_allowlist_entry skcipher_allowlist[] = {
|
||||
{ "adiantum(xchacha12,aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
||||
{ "adiantum(xchacha20,aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
||||
{ "cbc(aes)" }, /* iwd */
|
||||
{ "cbc(des)" }, /* iwd */
|
||||
{ "cbc(des3_ede)" }, /* iwd */
|
||||
{ "cbc(paes)" }, /* caam and others */
|
||||
{ "ctr(aes)" }, /* iwd */
|
||||
{ "ecb(aes)" }, /* iwd, bluez */
|
||||
{ "ecb(des)" }, /* iwd */
|
||||
{ "hctr2(aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
||||
{ "xts(aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup benchmark */
|
||||
{ "xts(camellia)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
||||
{ "xts(serpent)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
||||
{ "xts(twofish)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
||||
{},
|
||||
};
|
||||
|
||||
static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
size_t size)
|
||||
{
|
||||
@@ -311,6 +329,11 @@ static struct proto_ops algif_skcipher_ops_nokey = {
|
||||
static void *skcipher_bind(const char *name)
|
||||
{
|
||||
u32 mask = AF_ALG_CRYPTOAPI_MASK;
|
||||
int err;
|
||||
|
||||
err = af_alg_check_restriction(name, skcipher_allowlist);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
if (strcmp(name, "cbc(paes)") == 0)
|
||||
mask = 0;
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ struct crypto_larval *crypto_larval_alloc(const char *name, u32 type, u32 mask)
|
||||
larval->alg.cra_priority = -1;
|
||||
larval->alg.cra_destroy = crypto_larval_destroy;
|
||||
|
||||
strscpy(larval->alg.cra_name, name, CRYPTO_MAX_ALG_NAME);
|
||||
strscpy(larval->alg.cra_name, name);
|
||||
init_completion(&larval->completion);
|
||||
|
||||
return larval;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/cryptouser.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/string.h>
|
||||
#include <net/netlink.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/sock.h>
|
||||
@@ -87,11 +88,9 @@ static int crypto_report_one(struct crypto_alg *alg,
|
||||
{
|
||||
memset(ualg, 0, sizeof(*ualg));
|
||||
|
||||
strscpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name));
|
||||
strscpy(ualg->cru_driver_name, alg->cra_driver_name,
|
||||
sizeof(ualg->cru_driver_name));
|
||||
strscpy(ualg->cru_module_name, module_name(alg->cra_module),
|
||||
sizeof(ualg->cru_module_name));
|
||||
strscpy(ualg->cru_name, alg->cra_name);
|
||||
strscpy(ualg->cru_driver_name, alg->cra_driver_name);
|
||||
strscpy(ualg->cru_module_name, module_name(alg->cra_module));
|
||||
|
||||
ualg->cru_type = 0;
|
||||
ualg->cru_mask = 0;
|
||||
|
||||
+60
-38
@@ -279,6 +279,48 @@ static void vli_rshift1(u64 *vli, unsigned int ndigits)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __has_builtin
|
||||
#if __has_builtin(__builtin_addcll)
|
||||
#define USE_BUILTIN_ADDC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Computes result = left + right + carry_in and updates carry_out */
|
||||
static inline void add_carry(u64 left, u64 right, u64 *result, u64 carry_in,
|
||||
u64 *carry_out)
|
||||
{
|
||||
#ifdef USE_BUILTIN_ADDC
|
||||
*result = __builtin_addcll(left, right, carry_in, carry_out);
|
||||
#else
|
||||
u64 sum1, sum2;
|
||||
u64 c1 = __builtin_uaddll_overflow(left, right, &sum1);
|
||||
u64 c2 = __builtin_uaddll_overflow(sum1, carry_in, &sum2);
|
||||
*result = sum2;
|
||||
*carry_out = c1 | c2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __has_builtin
|
||||
#if __has_builtin(__builtin_subcll)
|
||||
#define USE_BUILTIN_SUBC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Computes result = left - right - borrow_in and updates borrow_out */
|
||||
static inline void sub_borrow(u64 left, u64 right, u64 *result, u64 borrow_in,
|
||||
u64 *borrow_out)
|
||||
{
|
||||
#ifdef USE_BUILTIN_SUBC
|
||||
*result = __builtin_subcll(left, right, borrow_in, borrow_out);
|
||||
#else
|
||||
u64 diff1, diff2;
|
||||
u64 b1 = __builtin_usubll_overflow(left, right, &diff1);
|
||||
u64 b2 = __builtin_usubll_overflow(diff1, borrow_in, &diff2);
|
||||
*result = diff2;
|
||||
*borrow_out = b1 | b2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Computes result = left + right, returning carry. Can modify in place. */
|
||||
static u64 vli_add(u64 *result, const u64 *left, const u64 *right,
|
||||
unsigned int ndigits)
|
||||
@@ -286,15 +328,8 @@ static u64 vli_add(u64 *result, const u64 *left, const u64 *right,
|
||||
u64 carry = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ndigits; i++) {
|
||||
u64 sum;
|
||||
|
||||
sum = left[i] + right[i] + carry;
|
||||
if (sum != left[i])
|
||||
carry = (sum < left[i]);
|
||||
|
||||
result[i] = sum;
|
||||
}
|
||||
for (i = 0; i < ndigits; i++)
|
||||
add_carry(left[i], right[i], &result[i], carry, &carry);
|
||||
|
||||
return carry;
|
||||
}
|
||||
@@ -303,40 +338,29 @@ static u64 vli_add(u64 *result, const u64 *left, const u64 *right,
|
||||
static u64 vli_uadd(u64 *result, const u64 *left, u64 right,
|
||||
unsigned int ndigits)
|
||||
{
|
||||
u64 carry = right;
|
||||
u64 carry;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ndigits; i++) {
|
||||
u64 sum;
|
||||
if (ndigits == 0)
|
||||
return right;
|
||||
|
||||
sum = left[i] + carry;
|
||||
if (sum != left[i])
|
||||
carry = (sum < left[i]);
|
||||
else
|
||||
carry = !!carry;
|
||||
carry = __builtin_uaddll_overflow(left[0], right, &result[0]);
|
||||
|
||||
result[i] = sum;
|
||||
}
|
||||
for (i = 1; i < ndigits; i++)
|
||||
carry = __builtin_uaddll_overflow(left[i], carry, &result[i]);
|
||||
|
||||
return carry;
|
||||
}
|
||||
|
||||
/* Computes result = left - right, returning borrow. Can modify in place. */
|
||||
u64 vli_sub(u64 *result, const u64 *left, const u64 *right,
|
||||
unsigned int ndigits)
|
||||
unsigned int ndigits)
|
||||
{
|
||||
u64 borrow = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ndigits; i++) {
|
||||
u64 diff;
|
||||
|
||||
diff = left[i] - right[i] - borrow;
|
||||
if (diff != left[i])
|
||||
borrow = (diff > left[i]);
|
||||
|
||||
result[i] = diff;
|
||||
}
|
||||
for (i = 0; i < ndigits; i++)
|
||||
sub_borrow(left[i], right[i], &result[i], borrow, &borrow);
|
||||
|
||||
return borrow;
|
||||
}
|
||||
@@ -344,20 +368,18 @@ EXPORT_SYMBOL(vli_sub);
|
||||
|
||||
/* Computes result = left - right, returning borrow. Can modify in place. */
|
||||
static u64 vli_usub(u64 *result, const u64 *left, u64 right,
|
||||
unsigned int ndigits)
|
||||
unsigned int ndigits)
|
||||
{
|
||||
u64 borrow = right;
|
||||
u64 borrow;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ndigits; i++) {
|
||||
u64 diff;
|
||||
if (ndigits == 0)
|
||||
return right;
|
||||
|
||||
diff = left[i] - borrow;
|
||||
if (diff != left[i])
|
||||
borrow = (diff > left[i]);
|
||||
borrow = __builtin_usubll_overflow(left[0], right, &result[0]);
|
||||
|
||||
result[i] = diff;
|
||||
}
|
||||
for (i = 1; i < ndigits; i++)
|
||||
borrow = __builtin_usubll_overflow(left[i], borrow, &result[i]);
|
||||
|
||||
return borrow;
|
||||
}
|
||||
|
||||
+1
-2
@@ -354,8 +354,7 @@ static int hctr2_create_common(struct crypto_template *tmpl, struct rtattr **tb,
|
||||
err = -EINVAL;
|
||||
if (strncmp(xctr_alg->base.cra_name, "xctr(", 5))
|
||||
goto err_free_inst;
|
||||
len = strscpy(blockcipher_name, xctr_alg->base.cra_name + 5,
|
||||
sizeof(blockcipher_name));
|
||||
len = strscpy(blockcipher_name, xctr_alg->base.cra_name + 5);
|
||||
if (len < 1)
|
||||
goto err_free_inst;
|
||||
if (blockcipher_name[len - 1] != ')')
|
||||
|
||||
@@ -263,10 +263,10 @@ struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *kr
|
||||
goto err;
|
||||
}
|
||||
|
||||
kfree(keys.data);
|
||||
kfree_sensitive(keys.data);
|
||||
return ci;
|
||||
err:
|
||||
kfree(keys.data);
|
||||
kfree_sensitive(keys.data);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL(crypto_krb5_prepare_encryption);
|
||||
@@ -333,10 +333,10 @@ struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb
|
||||
goto err;
|
||||
}
|
||||
|
||||
kfree(keys.data);
|
||||
kfree_sensitive(keys.data);
|
||||
return ci;
|
||||
err:
|
||||
kfree(keys.data);
|
||||
kfree_sensitive(keys.data);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL(crypto_krb5_prepare_checksum);
|
||||
|
||||
+1
-1
@@ -359,7 +359,7 @@ static int lrw_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
if (!memcmp(cipher_name, "ecb(", 4)) {
|
||||
int len;
|
||||
|
||||
len = strscpy(ecb_name, cipher_name + 4, sizeof(ecb_name));
|
||||
len = strscpy(ecb_name, cipher_name + 4);
|
||||
if (len < 2)
|
||||
goto err_free_inst;
|
||||
|
||||
|
||||
+1
-3
@@ -95,7 +95,6 @@ static int crypto_lskcipher_crypt_unaligned(
|
||||
|
||||
while (len >= bs) {
|
||||
unsigned chunk = min((unsigned)PAGE_SIZE, len);
|
||||
int err;
|
||||
|
||||
if (chunk > cs)
|
||||
chunk &= ~(cs - 1);
|
||||
@@ -528,8 +527,7 @@ struct lskcipher_instance *lskcipher_alloc_instance_simple(
|
||||
int len;
|
||||
|
||||
err = -EINVAL;
|
||||
len = strscpy(ecb_name, &cipher_alg->co.base.cra_name[4],
|
||||
sizeof(ecb_name));
|
||||
len = strscpy(ecb_name, &cipher_alg->co.base.cra_name[4]);
|
||||
if (len < 2)
|
||||
goto err_free_inst;
|
||||
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <crypto/xts.h>
|
||||
#include <crypto/b128ops.h>
|
||||
@@ -400,7 +401,7 @@ static int xts_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||||
if (!memcmp(cipher_name, "ecb(", 4)) {
|
||||
int len;
|
||||
|
||||
len = strscpy(name, cipher_name + 4, sizeof(name));
|
||||
len = strscpy(name, cipher_name + 4);
|
||||
if (len < 2)
|
||||
goto err_free_inst;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user