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