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

crypto: talitos - fix hash on SEC1.

On SEC1, hash provides wrong result when performing hashing in several
steps with input data SG list has more than one element. This was
detected with CONFIG_CRYPTO_MANAGER_EXTRA_TESTS:

[ 44.185947] alg: hash: md5-talitos test failed (wrong result) on test vector 6, cfg="random: may_sleep use_finup src_divs=[<reimport>25.88%@+8063, <flush>24.19%@+9588, 28.63%@+16333, <reimport>4.60%@+6756, 16.70%@+16281] dst_divs=[71.61%@alignmask+16361, 14.36%@+7756, 14.3%@+"
[ 44.325122] alg: hash: sha1-talitos test failed (wrong result) on test vector 3, cfg="random: inplace use_final src_divs=[<flush,nosimd>16.56%@+16378, <reimport>52.0%@+16329, 21.42%@alignmask+16380, 10.2%@alignmask+16380] iv_offset=39"
[ 44.493500] alg: hash: sha224-talitos test failed (wrong result) on test vector 4, cfg="random: use_final nosimd src_divs=[<reimport>52.27%@+7401, <reimport>17.34%@+16285, <flush>17.71%@+26, 12.68%@+10644] iv_offset=43"
[ 44.673262] alg: hash: sha256-talitos test failed (wrong result) on test vector 4, cfg="random: may_sleep use_finup src_divs=[<reimport>60.6%@+12790, 17.86%@+1329, <reimport>12.64%@alignmask+16300, 8.29%@+15, 0.40%@+13506, <reimport>0.51%@+16322, <reimport>0.24%@+16339] dst_divs"

This is due to two issues:
- We have an overlap between the buffer used for copying the input
data (SEC1 doesn't do scatter/gather) and the chained descriptor.
- Data copy is wrong when the previous hash left less than one
blocksize of data to hash, implying a complement of the previous
block with a few bytes from the new request.

Fix it by:
- Moving the second descriptor after the buffer, as moving the buffer
after the descriptor would make it more complex for other cipher
operations (AEAD, ABLKCIPHER)
- Skip the bytes taken from the new request to complete the previous
one by moving the SG list forward.

Fixes: 37b5e8897eb5 ("crypto: talitos - chain in buffered data for ahash on SEC1")
Cc: stable@vger.kernel.org
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Christophe Leroy and committed by
Herbert Xu
58cdbc6d d44769e4

+41 -28
+41 -28
drivers/crypto/talitos.c
··· 320 320 return -EINPROGRESS; 321 321 } 322 322 323 + static __be32 get_request_hdr(struct talitos_request *request, bool is_sec1) 324 + { 325 + struct talitos_edesc *edesc; 326 + 327 + if (!is_sec1) 328 + return request->desc->hdr; 329 + 330 + if (!request->desc->next_desc) 331 + return request->desc->hdr1; 332 + 333 + edesc = container_of(request->desc, struct talitos_edesc, desc); 334 + 335 + return ((struct talitos_desc *)(edesc->buf + edesc->dma_len))->hdr1; 336 + } 337 + 323 338 /* 324 339 * process what was done, notify callback of error if not 325 340 */ ··· 356 341 357 342 /* descriptors with their done bits set don't get the error */ 358 343 rmb(); 359 - if (!is_sec1) 360 - hdr = request->desc->hdr; 361 - else if (request->desc->next_desc) 362 - hdr = (request->desc + 1)->hdr1; 363 - else 364 - hdr = request->desc->hdr1; 344 + hdr = get_request_hdr(request, is_sec1); 365 345 366 346 if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE) 367 347 status = 0; ··· 486 476 } 487 477 } 488 478 489 - if (priv->chan[ch].fifo[iter].desc->next_desc == cur_desc) 490 - return (priv->chan[ch].fifo[iter].desc + 1)->hdr; 479 + if (priv->chan[ch].fifo[iter].desc->next_desc == cur_desc) { 480 + struct talitos_edesc *edesc; 481 + 482 + edesc = container_of(priv->chan[ch].fifo[iter].desc, 483 + struct talitos_edesc, desc); 484 + return ((struct talitos_desc *) 485 + (edesc->buf + edesc->dma_len))->hdr; 486 + } 491 487 492 488 return priv->chan[ch].fifo[iter].desc->hdr; 493 489 } ··· 1418 1402 edesc->dst_nents = dst_nents; 1419 1403 edesc->iv_dma = iv_dma; 1420 1404 edesc->dma_len = dma_len; 1421 - if (dma_len) { 1422 - void *addr = &edesc->link_tbl[0]; 1423 - 1424 - if (is_sec1 && !dst) 1425 - addr += sizeof(struct talitos_desc); 1426 - edesc->dma_link_tbl = dma_map_single(dev, addr, 1405 + if (dma_len) 1406 + edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0], 1427 1407 edesc->dma_len, 1428 1408 DMA_BIDIRECTIONAL); 1429 - } 1409 + 1430 1410 return edesc; 1431 1411 } 1432 1412 ··· 1734 1722 struct talitos_private *priv = dev_get_drvdata(dev); 1735 1723 bool is_sec1 = has_ftr_sec1(priv); 1736 1724 struct talitos_desc *desc = &edesc->desc; 1737 - struct talitos_desc *desc2 = desc + 1; 1725 + struct talitos_desc *desc2 = (struct talitos_desc *) 1726 + (edesc->buf + edesc->dma_len); 1738 1727 1739 1728 unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE); 1740 1729 if (desc->next_desc && 1741 1730 desc->ptr[5].ptr != desc2->ptr[5].ptr) 1742 1731 unmap_single_talitos_ptr(dev, &desc2->ptr[5], DMA_FROM_DEVICE); 1743 1732 1744 - talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0); 1733 + if (req_ctx->psrc) 1734 + talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0); 1745 1735 1746 1736 /* When using hashctx-in, must unmap it. */ 1747 1737 if (from_talitos_ptr_len(&edesc->desc.ptr[1], is_sec1)) ··· 1810 1796 1811 1797 static int common_nonsnoop_hash(struct talitos_edesc *edesc, 1812 1798 struct ahash_request *areq, unsigned int length, 1813 - unsigned int offset, 1814 1799 void (*callback) (struct device *dev, 1815 1800 struct talitos_desc *desc, 1816 1801 void *context, int error)) ··· 1848 1835 1849 1836 sg_count = edesc->src_nents ?: 1; 1850 1837 if (is_sec1 && sg_count > 1) 1851 - sg_pcopy_to_buffer(req_ctx->psrc, sg_count, 1852 - edesc->buf + sizeof(struct talitos_desc), 1853 - length, req_ctx->nbuf); 1838 + sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length); 1854 1839 else if (length) 1855 1840 sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count, 1856 1841 DMA_TO_DEVICE); ··· 1861 1850 DMA_TO_DEVICE); 1862 1851 } else { 1863 1852 sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, 1864 - &desc->ptr[3], sg_count, offset, 0); 1853 + &desc->ptr[3], sg_count, 0, 0); 1865 1854 if (sg_count > 1) 1866 1855 sync_needed = true; 1867 1856 } ··· 1885 1874 talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]); 1886 1875 1887 1876 if (is_sec1 && req_ctx->nbuf && length) { 1888 - struct talitos_desc *desc2 = desc + 1; 1877 + struct talitos_desc *desc2 = (struct talitos_desc *) 1878 + (edesc->buf + edesc->dma_len); 1889 1879 dma_addr_t next_desc; 1890 1880 1891 1881 memset(desc2, 0, sizeof(*desc2)); ··· 1907 1895 DMA_TO_DEVICE); 1908 1896 copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1); 1909 1897 sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc, 1910 - &desc2->ptr[3], sg_count, offset, 0); 1898 + &desc2->ptr[3], sg_count, 0, 0); 1911 1899 if (sg_count > 1) 1912 1900 sync_needed = true; 1913 1901 copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1); ··· 2018 2006 struct device *dev = ctx->dev; 2019 2007 struct talitos_private *priv = dev_get_drvdata(dev); 2020 2008 bool is_sec1 = has_ftr_sec1(priv); 2021 - int offset = 0; 2022 2009 u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx]; 2023 2010 2024 2011 if (!req_ctx->last && (nbytes + req_ctx->nbuf <= blocksize)) { ··· 2057 2046 sg_chain(req_ctx->bufsl, 2, areq->src); 2058 2047 req_ctx->psrc = req_ctx->bufsl; 2059 2048 } else if (is_sec1 && req_ctx->nbuf && req_ctx->nbuf < blocksize) { 2049 + int offset; 2050 + 2060 2051 if (nbytes_to_hash > blocksize) 2061 2052 offset = blocksize - req_ctx->nbuf; 2062 2053 else ··· 2071 2058 sg_copy_to_buffer(areq->src, nents, 2072 2059 ctx_buf + req_ctx->nbuf, offset); 2073 2060 req_ctx->nbuf += offset; 2074 - req_ctx->psrc = areq->src; 2061 + req_ctx->psrc = scatterwalk_ffwd(req_ctx->bufsl, areq->src, 2062 + offset); 2075 2063 } else 2076 2064 req_ctx->psrc = areq->src; 2077 2065 ··· 2112 2098 if (ctx->keylen && (req_ctx->first || req_ctx->last)) 2113 2099 edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC; 2114 2100 2115 - return common_nonsnoop_hash(edesc, areq, nbytes_to_hash, offset, 2116 - ahash_done); 2101 + return common_nonsnoop_hash(edesc, areq, nbytes_to_hash, ahash_done); 2117 2102 } 2118 2103 2119 2104 static int ahash_update(struct ahash_request *areq)