Merge tag 's390-7.2-6' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Vasily Gorbik:

 - Fix PCI MMIO write syscall falsely reporting success for mappings not
   valid for MMIO when MIO is unavailable by returning -EFAULT

 - Fix CPRB parameter buffer overflows in zcrypt CCA AES cipher and ECC
   private key conversion by rejecting oversized key tokens

 - Fix buffer overreads and length underflow in pkey and zcrypt CCA
   token validation by checking length fields against actual buffer
   sizes

 - Fix out of bounds permission bitmap access in zcrypt EP11 admin CPRB
   filtering on custom device nodes by using AP_DOMAINS as the limit

 - Fix speculative permission bitmap reads in zcrypt CCA and EP11 admin
   CPRB handling by sanitizing user controlled domain indexes

 - Fix sensitive key material left in zcrypt CCA clear key import
   buffers by scrubbing CPRB and temporary buffers after use

* tag 's390-7.2-6' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/zcrypt: Fix missing mem scrub at clear key import in cca_clr2cipherkey()
  s390/zcrypt: Close speculative mem read possibility
  s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs
  s390/zcrypt: Fix buffer over-read in cca_cipher2protkey
  s390/zcrypt: Validate length for CCA ECC private key requests
  s390/zcrypt: Validate length for CCA AES cipher key requests
  s390/pci: Fix s390_pci_mmio_write syscall error return without MIO
This commit is contained in:
Linus Torvalds
2026-08-02 11:55:49 -07:00
5 changed files with 85 additions and 30 deletions
+1
View File
@@ -188,6 +188,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr,
goto out_unlock_mmap;
}
ret = -EFAULT;
io_addr = (void __iomem *)((args.pfn << PAGE_SHIFT) |
(mmio_addr & ~PAGE_MASK));
+5 -10
View File
@@ -236,22 +236,16 @@ static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_AES) {
/* CCA AES data key */
if (keylen < sizeof(struct secaeskeytoken))
return -EINVAL;
if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0))
if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0))
return -EINVAL;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_VLSC) {
/* CCA AES cipher key */
if (keylen < hdr->len)
return -EINVAL;
if (cca_check_secaescipherkey(pkey_dbf_info,
3, key, 0, 1))
3, key, keylen, 0, 1))
return -EINVAL;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
/* CCA ECC (private) key */
if (keylen < sizeof(struct eccprivkeytoken))
return -EINVAL;
if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1))
return -EINVAL;
} else {
@@ -484,7 +478,7 @@ static int cca_verifykey(const u8 *key, u32 keylen,
hdr->version == TOKVER_CCA_AES) {
struct secaeskeytoken *t = (struct secaeskeytoken *)key;
rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0);
rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0);
if (rc)
goto out;
*keytype = PKEY_TYPE_CCA_DATA;
@@ -512,7 +506,8 @@ static int cca_verifykey(const u8 *key, u32 keylen,
hdr->version == TOKVER_CCA_VLSC) {
struct cipherkeytoken *t = (struct cipherkeytoken *)key;
rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1);
rc = cca_check_secaescipherkey(pkey_dbf_info, 3,
key, keylen, 0, 1);
if (rc)
goto out;
*keytype = PKEY_TYPE_CCA_CIPHER;
+3 -1
View File
@@ -879,6 +879,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
domain = array_index_nospec(domain, AP_DOMAINS);
if (!test_bit_inv(domain, perms->adm)) {
rc = -ENODEV;
goto out;
@@ -1077,8 +1078,9 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms,
print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1,
ap_msg.msg, ap_msg.len, false);
if (perms != &ap_perms && domain < AUTOSEL_DOM) {
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
domain = array_index_nospec(domain, AP_DOMAINS);
if (!test_bit_inv(domain, perms->adm)) {
rc = -ENODEV;
goto out;
+73 -16
View File
@@ -62,12 +62,18 @@ static DEFINE_MUTEX(dev_status_mem_mutex);
* also checked. Returns 0 on success or errno value on failure.
*/
int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
const u8 *token, int keybitsize)
const u8 *token, u32 keysize, int keybitsize)
{
struct secaeskeytoken *t = (struct secaeskeytoken *)token;
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
if (keysize < sizeof(*t)) {
if (dbg)
DBF("%s keysize %u < min token size %zu\n",
__func__, keysize, sizeof(*t));
return -EINVAL;
}
if (t->type != TOKTYPE_CCA_INTERNAL) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -101,14 +107,20 @@ EXPORT_SYMBOL(cca_check_secaeskeytoken);
* Returns 0 on success or errno value on failure.
*/
int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
const u8 *token, int keybitsize,
int checkcpacfexport)
const u8 *token, u32 keysize,
int keybitsize, int checkcpacfexport)
{
struct cipherkeytoken *t = (struct cipherkeytoken *)token;
bool keybitsizeok = true;
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
if (keysize < sizeof(*t)) {
if (dbg)
DBF("%s keysize %u < min token size %zu\n",
__func__, keysize, sizeof(*t));
return -EINVAL;
}
if (t->type != TOKTYPE_CCA_INTERNAL) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -121,6 +133,18 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
__func__, (int)t->version, TOKVER_CCA_VLSC);
return -EINVAL;
}
if (t->len > keysize) {
if (dbg)
DBF("%s token check failed, len %d > keysize %u\n",
__func__, (int)t->len, keysize);
return -EINVAL;
}
if (t->len < sizeof(*t)) {
if (dbg)
DBF("%s token check failed, len %d < min token size %zu\n",
__func__, (int)t->len, sizeof(*t));
return -EINVAL;
}
if (t->algtype != 0x02) {
if (dbg)
DBF("%s token check failed, algtype 0x%02x != 0x02\n",
@@ -195,6 +219,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
if (keysize < sizeof(*t)) {
if (dbg)
DBF("%s keysize %u < min token size %zu\n",
__func__, keysize, sizeof(*t));
return -EINVAL;
}
if (t->type != TOKTYPE_CCA_INTERNAL_PKA) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -207,6 +237,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
__func__, (int)t->len, keysize);
return -EINVAL;
}
if (t->len < sizeof(*t)) {
if (dbg)
DBF("%s token check failed, len %d < min token size %zu\n",
__func__, (int)t->len, sizeof(*t));
return -EINVAL;
}
if (t->secid != 0x20) {
if (dbg)
DBF("%s token check failed, secid 0x%02x != 0x20\n",
@@ -443,7 +479,8 @@ int cca_genseckey(u16 cardnr, u16 domain,
/* check secure key token */
rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
prepparm->lv3.keyblock.tok, 8 * keysize);
prepparm->lv3.keyblock.tok,
seckeysize, 8 * keysize);
if (rc) {
rc = -EIO;
goto out;
@@ -582,7 +619,8 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
/* check secure key token */
rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
prepparm->lv3.keyblock.tok, 8 * keysize);
prepparm->lv3.keyblock.tok,
seckeysize, 8 * keysize);
if (rc) {
rc = -EIO;
goto out;
@@ -842,6 +880,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
} kb;
} __packed * prepparm;
struct cipherkeytoken *t;
u32 keybuflen;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
@@ -936,23 +975,28 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
}
/* and some checks on the generated key */
t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key;
if (prepparm->kb.tlv1.len < 2 * sizeof(uint16_t) + sizeof(*t)) {
rc = -EIO;
goto out;
}
keybuflen = prepparm->kb.tlv1.len - 2 * sizeof(uint16_t);
rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR,
prepparm->kb.tlv1.gen_key,
keybitsize, 1);
keybuflen, keybitsize, 1);
if (rc) {
rc = -EIO;
goto out;
}
/* copy the generated vlsc key token */
t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key;
if (keybuf) {
if (*keybufsize >= t->len)
memcpy(keybuf, t, t->len);
if (*keybufsize >= keybuflen)
memcpy(keybuf, t, keybuflen);
else
rc = -EINVAL;
}
*keybufsize = t->len;
*keybufsize = keybuflen;
out:
free_cprbmem(mem, PARMBSIZE, false, xflags);
@@ -971,7 +1015,8 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
int clr_key_bit_size,
u8 *key_token,
int *key_token_size,
u32 xflags)
u32 xflags,
bool scrub)
{
int rc, n;
u8 *mem, *ptr;
@@ -1111,7 +1156,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
*key_token_size = t->len;
out:
free_cprbmem(mem, PARMBSIZE, false, xflags);
free_cprbmem(mem, PARMBSIZE, scrub, xflags);
return rc;
}
@@ -1162,28 +1207,32 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
* 4/4 COMPLETE the secure cipher key import
*/
rc = _ip_cprb_helper(card, dom, "AES ", "FIRST ", "MIN3PART",
exorbuf, keybitsize, token, &tokensize, xflags);
exorbuf, keybitsize, token, &tokensize,
xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL,
clrkey, keybitsize, token, &tokensize, xflags);
clrkey, keybitsize, token, &tokensize,
xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL,
exorbuf, keybitsize, token, &tokensize, xflags);
exorbuf, keybitsize, token, &tokensize,
xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "COMPLETE", NULL,
NULL, keybitsize, token, &tokensize, xflags);
NULL, keybitsize, token, &tokensize,
xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
@@ -1200,6 +1249,8 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
*keybufsize = tokensize;
out:
memzero_explicit(exorbuf, sizeof(exorbuf));
memzero_explicit(mem, CPRB_MEMPOOL_ITEM_SIZE);
mempool_free(mem, cprb_mempool);
return rc;
}
@@ -1261,6 +1312,9 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
} __packed * prepparm;
int keytoklen = ((struct cipherkeytoken *)ckey)->len;
if (keytoklen > PARMBSIZE - sizeof(struct aureqparm))
return -EINVAL;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
&preqcblk, &prepcblk, xflags);
@@ -1425,6 +1479,9 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
} __packed * prepparm;
int keylen = ((struct eccprivkeytoken *)key)->len;
if (keylen > PARMBSIZE - sizeof(struct aureqparm))
return -EINVAL;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
&preqcblk, &prepcblk, xflags);
+3 -3
View File
@@ -136,7 +136,7 @@ struct eccprivkeytoken {
* also checked. Returns 0 on success or errno value on failure.
*/
int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
const u8 *token, int keybitsize);
const u8 *token, u32 keysize, int keybitsize);
/*
* Simple check if the token is a valid CCA secure AES cipher key
@@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
* Returns 0 on success or errno value on failure.
*/
int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
const u8 *token, int keybitsize,
int checkcpacfexport);
const u8 *token, u32 keysize,
int keybitsize, int checkcpacfexport);
/*
* Simple check if the token is a valid CCA secure ECC private