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>
This commit is contained in:
parent
f47acc4b7c
commit
f4d6b94b40
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
const zstd_parameters params =
|
||||
zstd_get_params(aa_g_rawdata_compression_level, slen);
|
||||
const size_t wksp_len = zstd_cctx_workspace_bound(¶ms.cParams);
|
||||
void *wksp = NULL;
|
||||
zstd_cctx *ctx = NULL;
|
||||
size_t out_len = zstd_compress_bound(slen);
|
||||
void *out = NULL;
|
||||
int ret = 0;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
error = zlib_deflate(&strm, Z_FINISH);
|
||||
if (error != Z_STREAM_END) {
|
||||
error = -EINVAL;
|
||||
goto fail_deflate;
|
||||
ctx = zstd_init_cctx(wksp, wksp_len);
|
||||
if (!ctx) {
|
||||
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);
|
||||
out_len = zstd_compress_cctx(ctx, out, out_len, src, slen, ¶ms);
|
||||
if (zstd_is_error(out_len)) {
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!dstbuf) {
|
||||
error = -ENOMEM;
|
||||
goto fail_deflate;
|
||||
*dst = krealloc(out, out_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
*dst = dstbuf;
|
||||
*dlen = strm.total_out;
|
||||
if (!*dst) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fail_stg_alloc:
|
||||
zlib_deflateEnd(&strm);
|
||||
fail_deflate_init:
|
||||
kvfree(strm.workspace);
|
||||
return error;
|
||||
*dlen = out_len;
|
||||
|
||||
fail_deflate:
|
||||
kvfree(stgbuf);
|
||||
goto fail_stg_alloc;
|
||||
cleanup:
|
||||
if (ret) {
|
||||
kvfree(out);
|
||||
*dst = NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue