diff options
| author | Kees Cook <kees+treewide@kernel.org> | 2026-09-02 15:31:14 -0700 |
|---|---|---|
| committer | Kees Cook <kees@kernel.org> | 2026-09-04 21:37:00 -0700 |
| commit | 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d (patch) | |
| tree | c65086f9bdcd48c6360fb7cb4598bca084da1f32 /crypto/deflate.c | |
| download | linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.tar.gz linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.zip | |
treewide: refresh kmalloc_obj() conversionsgrafted
This is another run of the Coccinelle script for converting kmalloc()
family of allocations to kmalloc_obj() via the existing rules in
scripts/coccinelle/api/kmalloc_objs.cocci
This catches both the set of kmalloc() uses added since the first
kmalloc_obj() conversions in v7.0 and adds a large group missed in the
first pass due to Coccinelle not interacting well with the cleanup.h
scoped_...() family of macros[1]. I worked around this with spatch's
"--macro-file" argument to a file with all the scoped_...() macros mapped
to Coccinelle's YACFE_ITERATOR[2] as that was the closest viable control
flow indicator I could find.
Build tested allmodconfig on x86, arm64, arm, loongarch, mips, powerpc,
riscv, and s390 with no new warnings.
Link: https://lore.kernel.org/lkml/202609021314.8A9C0B8@keescook/ [1]
Link: https://github.com/coccinelle/coccinelle/blob/master/standard.h [2]
Signed-off-by: Kees Cook <kees+treewide@kernel.org>
Diffstat (limited to 'crypto/deflate.c')
| -rw-r--r-- | crypto/deflate.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/crypto/deflate.c b/crypto/deflate.c new file mode 100644 index 000000000..710ebba7c --- /dev/null +++ b/crypto/deflate.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cryptographic API. + * + * Deflate algorithm (RFC 1951), implemented here primarily for use + * by IPCOMP (RFC 3173 & RFC 2394). + * + * Copyright (c) 2003 James Morris <jmorris@intercode.com.au> + * Copyright (c) 2023 Google, LLC. <ardb@kernel.org> + * Copyright (c) 2025 Herbert Xu <herbert@gondor.apana.org.au> + */ +#include <crypto/internal/acompress.h> +#include <crypto/scatterwalk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/overflow.h> +#include <linux/percpu.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/zlib.h> + +#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION +#define DEFLATE_DEF_WINBITS 11 +#define DEFLATE_DEF_MEMLEVEL MAX_MEM_LEVEL + +struct deflate_stream { + struct z_stream_s stream; + u8 workspace[]; +}; + +static DEFINE_MUTEX(deflate_stream_lock); + +static void *deflate_alloc_stream(void) +{ + size_t size = max(zlib_inflate_workspacesize(), + zlib_deflate_workspacesize(-DEFLATE_DEF_WINBITS, + DEFLATE_DEF_MEMLEVEL)); + struct deflate_stream *ctx; + + ctx = kvmalloc_flex(*ctx, workspace, size); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->stream.workspace = ctx->workspace; + + return ctx; +} + +static void deflate_free_stream(void *ctx) +{ + kvfree(ctx); +} + +static struct crypto_acomp_streams deflate_streams = { + .alloc_ctx = deflate_alloc_stream, + .free_ctx = deflate_free_stream, +}; + +static int deflate_compress_one(struct acomp_req *req, + struct deflate_stream *ds) +{ + struct z_stream_s *stream = &ds->stream; + struct acomp_walk walk; + int ret; + + ret = acomp_walk_virt(&walk, req, true); + if (ret) + return ret; + + do { + unsigned int dcur; + + dcur = acomp_walk_next_dst(&walk); + if (!dcur) + return -ENOSPC; + + stream->avail_out = dcur; + stream->next_out = walk.dst.virt.addr; + + do { + int flush = Z_FINISH; + unsigned int scur; + + stream->avail_in = 0; + stream->next_in = NULL; + + scur = acomp_walk_next_src(&walk); + if (scur) { + if (acomp_walk_more_src(&walk, scur)) + flush = Z_NO_FLUSH; + stream->avail_in = scur; + stream->next_in = walk.src.virt.addr; + } + + ret = zlib_deflate(stream, flush); + + if (scur) { + scur -= stream->avail_in; + acomp_walk_done_src(&walk, scur); + } + } while (ret == Z_OK && stream->avail_out); + + acomp_walk_done_dst(&walk, dcur); + } while (ret == Z_OK); + + if (ret != Z_STREAM_END) + return -EINVAL; + + req->dlen = stream->total_out; + return 0; +} + +static int deflate_compress(struct acomp_req *req) +{ + struct crypto_acomp_stream *s; + struct deflate_stream *ds; + int err; + + s = crypto_acomp_lock_stream_bh(&deflate_streams); + ds = s->ctx; + + err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED, + -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, + Z_DEFAULT_STRATEGY); + if (err != Z_OK) { + err = -EINVAL; + goto out; + } + + err = deflate_compress_one(req, ds); + +out: + crypto_acomp_unlock_stream_bh(s); + + return err; +} + +static int deflate_decompress_one(struct acomp_req *req, + struct deflate_stream *ds) +{ + struct z_stream_s *stream = &ds->stream; + bool out_of_space = false; + struct acomp_walk walk; + int ret; + + ret = acomp_walk_virt(&walk, req, true); + if (ret) + return ret; + + do { + unsigned int scur; + + stream->avail_in = 0; + stream->next_in = NULL; + + scur = acomp_walk_next_src(&walk); + if (scur) { + stream->avail_in = scur; + stream->next_in = walk.src.virt.addr; + } + + do { + unsigned int dcur; + unsigned long avail_in; + + dcur = acomp_walk_next_dst(&walk); + + stream->avail_out = dcur; + stream->next_out = walk.dst.virt.addr; + avail_in = stream->avail_in; + + ret = zlib_inflate(stream, Z_NO_FLUSH); + + if (!dcur && avail_in == stream->avail_in) { + out_of_space = true; + break; + } + + dcur -= stream->avail_out; + acomp_walk_done_dst(&walk, dcur); + } while (ret == Z_OK && stream->avail_in); + + if (scur) + acomp_walk_done_src(&walk, scur); + + if (out_of_space) + return -ENOSPC; + } while (ret == Z_OK); + + if (ret != Z_STREAM_END) + return -EINVAL; + + req->dlen = stream->total_out; + return 0; +} + +static int deflate_decompress(struct acomp_req *req) +{ + struct crypto_acomp_stream *s; + struct deflate_stream *ds; + int err; + + s = crypto_acomp_lock_stream_bh(&deflate_streams); + ds = s->ctx; + + err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS); + if (err != Z_OK) { + err = -EINVAL; + goto out; + } + + err = deflate_decompress_one(req, ds); + +out: + crypto_acomp_unlock_stream_bh(s); + + return err; +} + +static int deflate_init(struct crypto_acomp *tfm) +{ + int ret; + + mutex_lock(&deflate_stream_lock); + ret = crypto_acomp_alloc_streams(&deflate_streams); + mutex_unlock(&deflate_stream_lock); + + return ret; +} + +static struct acomp_alg acomp = { + .compress = deflate_compress, + .decompress = deflate_decompress, + .init = deflate_init, + .base.cra_name = "deflate", + .base.cra_driver_name = "deflate-generic", + .base.cra_flags = CRYPTO_ALG_REQ_VIRT, + .base.cra_module = THIS_MODULE, +}; + +static int __init deflate_mod_init(void) +{ + return crypto_register_acomp(&acomp); +} + +static void __exit deflate_mod_fini(void) +{ + crypto_unregister_acomp(&acomp); + crypto_acomp_free_streams(&deflate_streams); +} + +module_init(deflate_mod_init); +module_exit(deflate_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); +MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); +MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>"); +MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>"); +MODULE_ALIAS_CRYPTO("deflate"); +MODULE_ALIAS_CRYPTO("deflate-generic"); |
