summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorJon Tourville <jon.tourville@canonical.com>2022-07-11 11:36:08 -0500
committerJohn Johansen <john.johansen@canonical.com>2022-10-03 14:49:02 -0700
commitf4d6b94b40c966ddd9eeb0d451e8a02c595ec7e3 (patch)
tree918cf11a9d4d6ccc6f362f4ee149586bff23c1fc /security
parentf47acc4b7c43d566bf42816335830c4c17f9c200 (diff)
downloadlinux-f4d6b94b40c966ddd9eeb0d451e8a02c595ec7e3.tar.gz
linux-f4d6b94b40c966ddd9eeb0d451e8a02c595ec7e3.tar.bz2
linux-f4d6b94b40c966ddd9eeb0d451e8a02c595ec7e3.zip
apparmor: use zstd compression for profile data
Change the algorithm used by apparmor to compress profile data from zlib to zstd, using the new zstd API introduced in 5.16. Zstd provides a larger range of compression levels than zlib and significantly better performance at the default level (for a relatively small increase in compressed size). The apparmor module parameter raw_data_compression_level is now clamped to the minimum and maximum compression levels reported by the zstd library. A compression level of 0 retains the previous behavior of disabling policy compression instead of using zstd's behavior, which is to use the default compression level. Signed-off-by: Jon Tourville <jon.tourville@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/Kconfig4
-rw-r--r--security/apparmor/apparmorfs.c60
-rw-r--r--security/apparmor/lsm.c10
-rw-r--r--security/apparmor/policy_unpack.c109
4 files changed, 81 insertions, 102 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index cb3496e00d8a..acac3bb3eef2 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -85,8 +85,8 @@ config SECURITY_APPARMOR_HASH_DEFAULT
config SECURITY_APPARMOR_EXPORT_BINARY
bool "Allow exporting the raw binary policy"
depends on SECURITY_APPARMOR_INTROSPECT_POLICY
- select ZLIB_INFLATE
- select ZLIB_DEFLATE
+ select ZSTD_COMPRESS
+ select ZSTD_DECOMPRESS
default y
help
This option allows reading back binary policy as it was loaded.
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 7160e7aa58b9..d98bbf267fc7 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -21,7 +21,7 @@
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/poll.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>
@@ -1297,42 +1297,30 @@ SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size);
-static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen)
+static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
{
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
- if (aa_g_rawdata_compression_level != 0) {
- int error = 0;
- struct z_stream_s strm;
-
- memset(&strm, 0, sizeof(strm));
-
- strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
- if (!strm.workspace)
- return -ENOMEM;
-
- strm.next_in = src;
- strm.avail_in = slen;
-
- error = zlib_inflateInit(&strm);
- if (error != Z_OK) {
- error = -ENOMEM;
- goto fail_inflate_init;
+ if (aa_g_rawdata_compression_level == 0) {
+ const size_t wksp_len = zstd_dctx_workspace_bound();
+ zstd_dctx *ctx;
+ void *wksp;
+ size_t out_len;
+ int ret = 0;
+
+ wksp = kvzalloc(wksp_len, GFP_KERNEL);
+ if (!wksp) {
+ ret = -ENOMEM;
+ goto cleanup;
}
- strm.next_out = dst;
- strm.avail_out = dlen;
-
- error = zlib_inflate(&strm, Z_FINISH);
- if (error != Z_STREAM_END)
- error = -EINVAL;
- else
- error = 0;
-
- zlib_inflateEnd(&strm);
-fail_inflate_init:
- kvfree(strm.workspace);
-
- return error;
+ out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
+ if (zstd_is_error(out_len)) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+cleanup:
+ kvfree(wksp);
+ return ret;
}
#endif
@@ -1381,9 +1369,9 @@ static int rawdata_open(struct inode *inode, struct file *file)
private->loaddata = loaddata;
- error = deflate_decompress(loaddata->data, loaddata->compressed_size,
- RAWDATA_F_DATA_BUF(private),
- loaddata->size);
+ error = decompress_zstd(loaddata->data, loaddata->compressed_size,
+ RAWDATA_F_DATA_BUF(private),
+ loaddata->size);
if (error)
goto fail_decompress;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index e29cade7b662..ec873ff0a4bb 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -21,7 +21,7 @@
#include <linux/user_namespace.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
#include <net/sock.h>
#include <uapi/linux/mount.h>
@@ -1361,7 +1361,7 @@ module_param_named(export_binary, aa_g_export_binary, aabool, 0600);
#endif
/* policy loaddata compression level */
-int aa_g_rawdata_compression_level = Z_DEFAULT_COMPRESSION;
+int aa_g_rawdata_compression_level = ZSTD_CLEVEL_DEFAULT;
module_param_named(rawdata_compression_level, aa_g_rawdata_compression_level,
aacompressionlevel, 0400);
@@ -1543,9 +1543,9 @@ static int param_set_aacompressionlevel(const char *val,
error = param_set_int(val, kp);
aa_g_rawdata_compression_level = clamp(aa_g_rawdata_compression_level,
- Z_NO_COMPRESSION,
- Z_BEST_COMPRESSION);
- pr_info("AppArmor: policy rawdata compression level set to %u\n",
+ zstd_min_clevel(),
+ zstd_max_clevel());
+ pr_info("AppArmor: policy rawdata compression level set to %d\n",
aa_g_rawdata_compression_level);
return error;
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 55d31bac4f35..10e462d00321 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -16,7 +16,7 @@
#include <asm/unaligned.h>
#include <linux/ctype.h>
#include <linux/errno.h>
-#include <linux/zlib.h>
+#include <linux/zstd.h>
#include "include/apparmor.h"
#include "include/audit.h"
@@ -1059,81 +1059,73 @@ struct aa_load_ent *aa_load_ent_alloc(void)
return ent;
}
-static int deflate_compress(const char *src, size_t slen, char **dst,
- size_t *dlen)
+static int compress_zstd(const char *src, size_t slen, char **dst, size_t *dlen)
{
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
- int error;
- struct z_stream_s strm;
- void *stgbuf, *dstbuf;
- size_t stglen = deflateBound(slen);
-
- memset(&strm, 0, sizeof(strm));
-
- if (stglen < slen)
- return -EFBIG;
-
- strm.workspace = kvzalloc(zlib_deflate_workspacesize(MAX_WBITS,
- MAX_MEM_LEVEL),
- GFP_KERNEL);
- if (!strm.workspace)
- return -ENOMEM;
-
- error = zlib_deflateInit(&strm, aa_g_rawdata_compression_level);
- if (error != Z_OK) {
- error = -ENOMEM;
- goto fail_deflate_init;
+ const zstd_parameters params =
+ zstd_get_params(aa_g_rawdata_compression_level, slen);
+ const size_t wksp_len = zstd_cctx_workspace_bound(&params.cParams);
+ void *wksp = NULL;
+ zstd_cctx *ctx = NULL;
+ size_t out_len = zstd_compress_bound(slen);
+ void *out = NULL;
+ int ret = 0;
+
+ out = kvzalloc(out_len, GFP_KERNEL);
+ if (!out) {
+ ret = -ENOMEM;
+ goto cleanup;
}
- stgbuf = kvzalloc(stglen, GFP_KERNEL);
- if (!stgbuf) {
- error = -ENOMEM;
- goto fail_stg_alloc;
+ wksp = kvzalloc(wksp_len, GFP_KERNEL);
+ if (!wksp) {
+ ret = -ENOMEM;
+ goto cleanup;
}
- strm.next_in = src;
- strm.avail_in = slen;
- strm.next_out = stgbuf;
- strm.avail_out = stglen;
+ ctx = zstd_init_cctx(wksp, wksp_len);
+ if (!ctx) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
- error = zlib_deflate(&strm, Z_FINISH);
- if (error != Z_STREAM_END) {
- error = -EINVAL;
- goto fail_deflate;
+ out_len = zstd_compress_cctx(ctx, out, out_len, src, slen, &params);
+ if (zstd_is_error(out_len)) {
+ ret = -EINVAL;
+ goto cleanup;
}
- error = 0;
- if (is_vmalloc_addr(stgbuf)) {
- dstbuf = kvzalloc(strm.total_out, GFP_KERNEL);
- if (dstbuf) {
- memcpy(dstbuf, stgbuf, strm.total_out);
- kvfree(stgbuf);
+ if (is_vmalloc_addr(out)) {
+ *dst = kvzalloc(out_len, GFP_KERNEL);
+ if (*dst) {
+ memcpy(*dst, out, out_len);
+ kvfree(out);
+ out = NULL;
}
- } else
+ } else {
/*
* If the staging buffer was kmalloc'd, then using krealloc is
* probably going to be faster. The destination buffer will
* always be smaller, so it's just shrunk, avoiding a memcpy
*/
- dstbuf = krealloc(stgbuf, strm.total_out, GFP_KERNEL);
+ *dst = krealloc(out, out_len, GFP_KERNEL);
+ }
- if (!dstbuf) {
- error = -ENOMEM;
- goto fail_deflate;
+ if (!*dst) {
+ ret = -ENOMEM;
+ goto cleanup;
}
- *dst = dstbuf;
- *dlen = strm.total_out;
+ *dlen = out_len;
-fail_stg_alloc:
- zlib_deflateEnd(&strm);
-fail_deflate_init:
- kvfree(strm.workspace);
- return error;
+cleanup:
+ if (ret) {
+ kvfree(out);
+ *dst = NULL;
+ }
-fail_deflate:
- kvfree(stgbuf);
- goto fail_stg_alloc;
+ kvfree(wksp);
+ return ret;
#else
*dlen = slen;
return 0;
@@ -1142,7 +1134,6 @@ fail_deflate:
static int compress_loaddata(struct aa_loaddata *data)
{
-
AA_BUG(data->compressed_size > 0);
/*
@@ -1151,8 +1142,8 @@ static int compress_loaddata(struct aa_loaddata *data)
*/
if (aa_g_rawdata_compression_level != 0) {
void *udata = data->data;
- int error = deflate_compress(udata, data->size, &data->data,
- &data->compressed_size);
+ int error = compress_zstd(udata, data->size, &data->data,
+ &data->compressed_size);
if (error)
return error;