xref: /linux/crypto/deflate.c (revision a619fe35ab41fded440d3762d4fbad84ff86a4d4)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Cryptographic API.
4  *
5  * Deflate algorithm (RFC 1951), implemented here primarily for use
6  * by IPCOMP (RFC 3173 & RFC 2394).
7  *
8  * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
9  * Copyright (c) 2023 Google, LLC. <ardb@kernel.org>
10  * Copyright (c) 2025 Herbert Xu <herbert@gondor.apana.org.au>
11  */
12 #include <crypto/internal/acompress.h>
13 #include <crypto/scatterwalk.h>
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/mutex.h>
18 #include <linux/overflow.h>
19 #include <linux/percpu.h>
20 #include <linux/scatterlist.h>
21 #include <linux/slab.h>
22 #include <linux/spinlock.h>
23 #include <linux/zlib.h>
24 
25 #define DEFLATE_DEF_LEVEL		Z_DEFAULT_COMPRESSION
26 #define DEFLATE_DEF_WINBITS		11
27 #define DEFLATE_DEF_MEMLEVEL		MAX_MEM_LEVEL
28 
29 struct deflate_stream {
30 	struct z_stream_s stream;
31 	u8 workspace[];
32 };
33 
34 static DEFINE_MUTEX(deflate_stream_lock);
35 
deflate_alloc_stream(void)36 static void *deflate_alloc_stream(void)
37 {
38 	size_t size = max(zlib_inflate_workspacesize(),
39 			  zlib_deflate_workspacesize(-DEFLATE_DEF_WINBITS,
40 						     DEFLATE_DEF_MEMLEVEL));
41 	struct deflate_stream *ctx;
42 
43 	ctx = kvmalloc(struct_size(ctx, workspace, size), GFP_KERNEL);
44 	if (!ctx)
45 		return ERR_PTR(-ENOMEM);
46 
47 	ctx->stream.workspace = ctx->workspace;
48 
49 	return ctx;
50 }
51 
deflate_free_stream(void * ctx)52 static void deflate_free_stream(void *ctx)
53 {
54 	kvfree(ctx);
55 }
56 
57 static struct crypto_acomp_streams deflate_streams = {
58 	.alloc_ctx = deflate_alloc_stream,
59 	.free_ctx = deflate_free_stream,
60 };
61 
deflate_compress_one(struct acomp_req * req,struct deflate_stream * ds)62 static int deflate_compress_one(struct acomp_req *req,
63 				struct deflate_stream *ds)
64 {
65 	struct z_stream_s *stream = &ds->stream;
66 	struct acomp_walk walk;
67 	int ret;
68 
69 	ret = acomp_walk_virt(&walk, req, true);
70 	if (ret)
71 		return ret;
72 
73 	do {
74 		unsigned int dcur;
75 
76 		dcur = acomp_walk_next_dst(&walk);
77 		if (!dcur)
78 			return -ENOSPC;
79 
80 		stream->avail_out = dcur;
81 		stream->next_out = walk.dst.virt.addr;
82 
83 		do {
84 			int flush = Z_FINISH;
85 			unsigned int scur;
86 
87 			stream->avail_in = 0;
88 			stream->next_in = NULL;
89 
90 			scur = acomp_walk_next_src(&walk);
91 			if (scur) {
92 				if (acomp_walk_more_src(&walk, scur))
93 					flush = Z_NO_FLUSH;
94 				stream->avail_in = scur;
95 				stream->next_in = walk.src.virt.addr;
96 			}
97 
98 			ret = zlib_deflate(stream, flush);
99 
100 			if (scur) {
101 				scur -= stream->avail_in;
102 				acomp_walk_done_src(&walk, scur);
103 			}
104 		} while (ret == Z_OK && stream->avail_out);
105 
106 		acomp_walk_done_dst(&walk, dcur);
107 	} while (ret == Z_OK);
108 
109 	if (ret != Z_STREAM_END)
110 		return -EINVAL;
111 
112 	req->dlen = stream->total_out;
113 	return 0;
114 }
115 
deflate_compress(struct acomp_req * req)116 static int deflate_compress(struct acomp_req *req)
117 {
118 	struct crypto_acomp_stream *s;
119 	struct deflate_stream *ds;
120 	int err;
121 
122 	s = crypto_acomp_lock_stream_bh(&deflate_streams);
123 	ds = s->ctx;
124 
125 	err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
126 				-DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
127 				Z_DEFAULT_STRATEGY);
128 	if (err != Z_OK) {
129 		err = -EINVAL;
130 		goto out;
131 	}
132 
133 	err = deflate_compress_one(req, ds);
134 
135 out:
136 	crypto_acomp_unlock_stream_bh(s);
137 
138 	return err;
139 }
140 
deflate_decompress_one(struct acomp_req * req,struct deflate_stream * ds)141 static int deflate_decompress_one(struct acomp_req *req,
142 				  struct deflate_stream *ds)
143 {
144 	struct z_stream_s *stream = &ds->stream;
145 	bool out_of_space = false;
146 	struct acomp_walk walk;
147 	int ret;
148 
149 	ret = acomp_walk_virt(&walk, req, true);
150 	if (ret)
151 		return ret;
152 
153 	do {
154 		unsigned int scur;
155 
156 		stream->avail_in = 0;
157 		stream->next_in = NULL;
158 
159 		scur = acomp_walk_next_src(&walk);
160 		if (scur) {
161 			stream->avail_in = scur;
162 			stream->next_in = walk.src.virt.addr;
163 		}
164 
165 		do {
166 			unsigned int dcur;
167 
168 			dcur = acomp_walk_next_dst(&walk);
169 			if (!dcur) {
170 				out_of_space = true;
171 				break;
172 			}
173 
174 			stream->avail_out = dcur;
175 			stream->next_out = walk.dst.virt.addr;
176 
177 			ret = zlib_inflate(stream, Z_NO_FLUSH);
178 
179 			dcur -= stream->avail_out;
180 			acomp_walk_done_dst(&walk, dcur);
181 		} while (ret == Z_OK && stream->avail_in);
182 
183 		if (scur)
184 			acomp_walk_done_src(&walk, scur);
185 
186 		if (out_of_space)
187 			return -ENOSPC;
188 	} while (ret == Z_OK);
189 
190 	if (ret != Z_STREAM_END)
191 		return -EINVAL;
192 
193 	req->dlen = stream->total_out;
194 	return 0;
195 }
196 
deflate_decompress(struct acomp_req * req)197 static int deflate_decompress(struct acomp_req *req)
198 {
199 	struct crypto_acomp_stream *s;
200 	struct deflate_stream *ds;
201 	int err;
202 
203 	s = crypto_acomp_lock_stream_bh(&deflate_streams);
204 	ds = s->ctx;
205 
206 	err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS);
207 	if (err != Z_OK) {
208 		err = -EINVAL;
209 		goto out;
210 	}
211 
212 	err = deflate_decompress_one(req, ds);
213 
214 out:
215 	crypto_acomp_unlock_stream_bh(s);
216 
217 	return err;
218 }
219 
deflate_init(struct crypto_acomp * tfm)220 static int deflate_init(struct crypto_acomp *tfm)
221 {
222 	int ret;
223 
224 	mutex_lock(&deflate_stream_lock);
225 	ret = crypto_acomp_alloc_streams(&deflate_streams);
226 	mutex_unlock(&deflate_stream_lock);
227 
228 	return ret;
229 }
230 
231 static struct acomp_alg acomp = {
232 	.compress		= deflate_compress,
233 	.decompress		= deflate_decompress,
234 	.init			= deflate_init,
235 	.base.cra_name		= "deflate",
236 	.base.cra_driver_name	= "deflate-generic",
237 	.base.cra_flags		= CRYPTO_ALG_REQ_VIRT,
238 	.base.cra_module	= THIS_MODULE,
239 };
240 
deflate_mod_init(void)241 static int __init deflate_mod_init(void)
242 {
243 	return crypto_register_acomp(&acomp);
244 }
245 
deflate_mod_fini(void)246 static void __exit deflate_mod_fini(void)
247 {
248 	crypto_unregister_acomp(&acomp);
249 	crypto_acomp_free_streams(&deflate_streams);
250 }
251 
252 module_init(deflate_mod_init);
253 module_exit(deflate_mod_fini);
254 
255 MODULE_LICENSE("GPL");
256 MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");
257 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
258 MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>");
259 MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>");
260 MODULE_ALIAS_CRYPTO("deflate");
261 MODULE_ALIAS_CRYPTO("deflate-generic");
262