]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
LSM: hide struct security_mnt_opts from any generic code
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 13 Dec 2018 18:41:47 +0000 (13:41 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 21 Dec 2018 16:48:34 +0000 (11:48 -0500)
Keep void * instead, allocate on demand (in parse_str_opts, at the
moment).  Eventually both selinux and smack will be better off
with private structures with several strings in those, rather than
this "counter and two pointers to dynamically allocated arrays"
ugliness.  This commit allows to do that at leisure, without
disrupting anything outside of given module.

Changes:
* instead of struct security_mnt_opt use an opaque pointer
initialized to NULL.
* security_sb_eat_lsm_opts(), security_sb_parse_opts_str() and
security_free_mnt_opts() take it as var argument (i.e. as void **);
call sites are unchanged.
* security_sb_set_mnt_opts() and security_sb_remount() take
it by value (i.e. as void *).
* new method: ->sb_free_mnt_opts().  Takes void *, does
whatever freeing that needs to be done.
* ->sb_set_mnt_opts() and ->sb_remount() might get NULL as
mnt_opts argument, meaning "empty".

Reviewed-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/btrfs/super.c
fs/namespace.c
fs/nfs/internal.h
fs/nfs/super.c
fs/super.c
include/linux/lsm_hooks.h
include/linux/security.h
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c

index 3b04e7735b5fd064dc9d11c21b65e2f0451d788b..e90c4616ed6ae7b326a669ddf6246cb4d0dac4bb 100644 (file)
@@ -1472,14 +1472,13 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
        struct btrfs_device *device = NULL;
        struct btrfs_fs_devices *fs_devices = NULL;
        struct btrfs_fs_info *fs_info = NULL;
-       struct security_mnt_opts new_sec_opts;
+       void *new_sec_opts = NULL;
        fmode_t mode = FMODE_READ;
        int error = 0;
 
        if (!(flags & SB_RDONLY))
                mode |= FMODE_WRITE;
 
-       security_init_mnt_opts(&new_sec_opts);
        if (data) {
                error = security_sb_eat_lsm_opts(data, &new_sec_opts);
                if (error)
@@ -1551,7 +1550,7 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
                error = btrfs_fill_super(s, fs_devices, data);
        }
        if (!error)
-               error = security_sb_set_mnt_opts(s, &new_sec_opts, 0, NULL);
+               error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
        security_free_mnt_opts(&new_sec_opts);
        if (error) {
                deactivate_locked_super(s);
@@ -1724,12 +1723,11 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
        btrfs_remount_prepare(fs_info);
 
        if (data) {
-               struct security_mnt_opts new_sec_opts;
+               void *new_sec_opts = NULL;
 
-               security_init_mnt_opts(&new_sec_opts);
                ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
                if (!ret)
-                       ret = security_sb_remount(sb, &new_sec_opts);
+                       ret = security_sb_remount(sb, new_sec_opts);
                security_free_mnt_opts(&new_sec_opts);
                if (ret)
                        goto restore;
index 39aca7b69c2e3a9cfa86abc6768463ec640a774b..badfd287358c0da88e75ab563c817ddaf813f8d4 100644 (file)
@@ -2299,7 +2299,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
        int err;
        struct super_block *sb = path->mnt->mnt_sb;
        struct mount *mnt = real_mount(path->mnt);
-       struct security_mnt_opts opts;
+       void *sec_opts = NULL;
 
        if (!check_mnt(mnt))
                return -EINVAL;
@@ -2310,14 +2310,13 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
        if (!can_change_locked_flags(mnt, mnt_flags))
                return -EPERM;
 
-       security_init_mnt_opts(&opts);
        if (data && !(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)) {
-               err = security_sb_eat_lsm_opts(data, &opts);
+               err = security_sb_eat_lsm_opts(data, &sec_opts);
                if (err)
                        return err;
        }
-       err = security_sb_remount(sb, &opts);
-       security_free_mnt_opts(&opts);
+       err = security_sb_remount(sb, sec_opts);
+       security_free_mnt_opts(&sec_opts);
        if (err)
                return err;
 
index 8357ff69962f22caae59d1ac357058d89800e963..97e1dcefe561cb65aa685d575ef3f5f574032dfd 100644 (file)
@@ -123,7 +123,7 @@ struct nfs_parsed_mount_data {
                unsigned short          protocol;
        } nfs_server;
 
-       struct security_mnt_opts lsm_opts;
+       void                    *lsm_opts;
        struct net              *net;
 };
 
index 300bdd1d4a093db40b88341e36f08ac5744c4583..1943de8f9d2967dc3abfe5e606dc4d48f60edd3b 100644 (file)
@@ -929,7 +929,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
                data->minorversion      = 0;
                data->need_mount        = true;
                data->net               = current->nsproxy->net_ns;
-               security_init_mnt_opts(&data->lsm_opts);
+               data->lsm_opts          = NULL;
        }
        return data;
 }
@@ -2294,7 +2294,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
        /* compare new mount options with old ones */
        error = nfs_compare_remount_data(nfss, data);
        if (!error)
-               error = security_sb_remount(sb, &data->lsm_opts);
+               error = security_sb_remount(sb, data->lsm_opts);
 out:
        nfs_free_parsed_mount_data(data);
        return error;
@@ -2534,7 +2534,7 @@ int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
        if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
                kflags |= SECURITY_LSM_NATIVE_LABELS;
 
-       error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts,
+       error = security_sb_set_mnt_opts(s, mount_info->parsed->lsm_opts,
                                                kflags, &kflags_out);
        if (error)
                goto err;
index 1f75fe3125977089db8f1416f3a3fa6fda6f1b46..a5511c4ba69bce481454d8a9fd75f14e96b91fc5 100644 (file)
@@ -1247,12 +1247,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
        struct dentry *root;
        struct super_block *sb;
        int error = -ENOMEM;
-       struct security_mnt_opts opts;
-
-       security_init_mnt_opts(&opts);
+       void *sec_opts = NULL;
 
        if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
-               error = security_sb_eat_lsm_opts(data, &opts);
+               error = security_sb_eat_lsm_opts(data, &sec_opts);
                if (error)
                        return ERR_PTR(error);
        }
@@ -1275,7 +1273,7 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
        smp_wmb();
        sb->s_flags |= SB_BORN;
 
-       error = security_sb_set_mnt_opts(sb, &opts, 0, NULL);
+       error = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
        if (error)
                goto out_sb;
 
@@ -1295,13 +1293,13 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
                "negative value (%lld)\n", type->name, sb->s_maxbytes);
 
        up_write(&sb->s_umount);
-       security_free_mnt_opts(&opts);
+       security_free_mnt_opts(&sec_opts);
        return root;
 out_sb:
        dput(root);
        deactivate_locked_super(sb);
 out_free_secdata:
-       security_free_mnt_opts(&opts);
+       security_free_mnt_opts(&sec_opts);
        return ERR_PTR(error);
 }
 
index c418909c178c19f0281a39b4358ffdd6c2297a14..a9c541f5732e5682fb464ca14a84dd842c2744cd 100644 (file)
@@ -1461,9 +1461,9 @@ union security_list_options {
 
        int (*sb_alloc_security)(struct super_block *sb);
        void (*sb_free_security)(struct super_block *sb);
-       int (*sb_eat_lsm_opts)(char *orig, struct security_mnt_opts *opts);
-       int (*sb_remount)(struct super_block *sb,
-                         struct security_mnt_opts *opts);
+       void (*sb_free_mnt_opts)(void *mnt_opts);
+       int (*sb_eat_lsm_opts)(char *orig, void **mnt_opts);
+       int (*sb_remount)(struct super_block *sb, void *mnt_opts);
        int (*sb_kern_mount)(struct super_block *sb);
        int (*sb_show_options)(struct seq_file *m, struct super_block *sb);
        int (*sb_statfs)(struct dentry *dentry);
@@ -1472,14 +1472,14 @@ union security_list_options {
        int (*sb_umount)(struct vfsmount *mnt, int flags);
        int (*sb_pivotroot)(const struct path *old_path, const struct path *new_path);
        int (*sb_set_mnt_opts)(struct super_block *sb,
-                               struct security_mnt_opts *opts,
+                               void *mnt_opts,
                                unsigned long kern_flags,
                                unsigned long *set_kern_flags);
        int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
                                        struct super_block *newsb,
                                        unsigned long kern_flags,
                                        unsigned long *set_kern_flags);
-       int (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
+       int (*sb_parse_opts_str)(char *options, void **mnt_opts);
        int (*dentry_init_security)(struct dentry *dentry, int mode,
                                        const struct qstr *name, void **ctx,
                                        u32 *ctxlen);
@@ -1801,6 +1801,7 @@ struct security_hook_heads {
        struct hlist_head bprm_committed_creds;
        struct hlist_head sb_alloc_security;
        struct hlist_head sb_free_security;
+       struct hlist_head sb_free_mnt_opts;
        struct hlist_head sb_eat_lsm_opts;
        struct hlist_head sb_remount;
        struct hlist_head sb_kern_mount;
index d000933635701897960cad0b9d02dcde82e314ff..4bca0be95b7a7dacfd18a0ab1f5f327668f88e00 100644 (file)
@@ -192,26 +192,6 @@ int call_lsm_notifier(enum lsm_event event, void *data);
 int register_lsm_notifier(struct notifier_block *nb);
 int unregister_lsm_notifier(struct notifier_block *nb);
 
-static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
-{
-       opts->mnt_opts = NULL;
-       opts->mnt_opts_flags = NULL;
-       opts->num_mnt_opts = 0;
-}
-
-static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
-{
-       int i;
-       if (opts->mnt_opts)
-               for (i = 0; i < opts->num_mnt_opts; i++)
-                       kfree(opts->mnt_opts[i]);
-       kfree(opts->mnt_opts);
-       opts->mnt_opts = NULL;
-       kfree(opts->mnt_opts_flags);
-       opts->mnt_opts_flags = NULL;
-       opts->num_mnt_opts = 0;
-}
-
 /* prototypes */
 extern int security_init(void);
 
@@ -248,8 +228,9 @@ void security_bprm_committing_creds(struct linux_binprm *bprm);
 void security_bprm_committed_creds(struct linux_binprm *bprm);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
-int security_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts);
-int security_sb_remount(struct super_block *sb, struct security_mnt_opts *opts);
+void security_free_mnt_opts(void **mnt_opts);
+int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
+int security_sb_remount(struct super_block *sb, void *mnt_opts);
 int security_sb_kern_mount(struct super_block *sb);
 int security_sb_show_options(struct seq_file *m, struct super_block *sb);
 int security_sb_statfs(struct dentry *dentry);
@@ -258,14 +239,14 @@ int security_sb_mount(const char *dev_name, const struct path *path,
 int security_sb_umount(struct vfsmount *mnt, int flags);
 int security_sb_pivotroot(const struct path *old_path, const struct path *new_path);
 int security_sb_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts,
+                               void *mnt_opts,
                                unsigned long kern_flags,
                                unsigned long *set_kern_flags);
 int security_sb_clone_mnt_opts(const struct super_block *oldsb,
                                struct super_block *newsb,
                                unsigned long kern_flags,
                                unsigned long *set_kern_flags);
-int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
+int security_sb_parse_opts_str(char *options, void **mnt_opts);
 int security_dentry_init_security(struct dentry *dentry, int mode,
                                        const struct qstr *name, void **ctx,
                                        u32 *ctxlen);
@@ -421,11 +402,7 @@ static inline  int unregister_lsm_notifier(struct notifier_block *nb)
        return 0;
 }
 
-static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
-{
-}
-
-static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
+static inline void security_free_mnt_opts(void **mnt_opts)
 {
 }
 
@@ -556,13 +533,13 @@ static inline void security_sb_free(struct super_block *sb)
 { }
 
 static inline int security_sb_eat_lsm_opts(char *options,
-                                          struct security_mnt_opts *opts)
+                                          void **mnt_opts)
 {
        return 0;
 }
 
 static inline int security_sb_remount(struct super_block *sb,
-                                     struct security_mnt_opts *opts)
+                                     void *mnt_opts)
 {
        return 0;
 }
@@ -602,7 +579,7 @@ static inline int security_sb_pivotroot(const struct path *old_path,
 }
 
 static inline int security_sb_set_mnt_opts(struct super_block *sb,
-                                          struct security_mnt_opts *opts,
+                                          void *mnt_opts,
                                           unsigned long kern_flags,
                                           unsigned long *set_kern_flags)
 {
@@ -617,7 +594,7 @@ static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb,
        return 0;
 }
 
-static inline int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+static inline int security_sb_parse_opts_str(char *options, void **mnt_opts)
 {
        return 0;
 }
index feb18c925349b1bdddae2cb0aa3ac275286b8bbc..b7a5a00518075eb8fb5edf6c24c6f6c48a736099 100644 (file)
@@ -384,16 +384,25 @@ void security_sb_free(struct super_block *sb)
        call_void_hook(sb_free_security, sb);
 }
 
-int security_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts)
+void security_free_mnt_opts(void **mnt_opts)
 {
-       return call_int_hook(sb_eat_lsm_opts, 0, options, opts);
+       if (!*mnt_opts)
+               return;
+       call_void_hook(sb_free_mnt_opts, *mnt_opts);
+       *mnt_opts = NULL;
+}
+EXPORT_SYMBOL(security_free_mnt_opts);
+
+int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
+{
+       return call_int_hook(sb_eat_lsm_opts, 0, options, mnt_opts);
 }
 EXPORT_SYMBOL(security_sb_eat_lsm_opts);
 
 int security_sb_remount(struct super_block *sb,
-                       struct security_mnt_opts *opts)
+                       void *mnt_opts)
 {
-       return call_int_hook(sb_remount, 0, sb, opts);
+       return call_int_hook(sb_remount, 0, sb, mnt_opts);
 }
 EXPORT_SYMBOL(security_sb_remount);
 
@@ -429,13 +438,13 @@ int security_sb_pivotroot(const struct path *old_path, const struct path *new_pa
 }
 
 int security_sb_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts,
+                               void *mnt_opts,
                                unsigned long kern_flags,
                                unsigned long *set_kern_flags)
 {
        return call_int_hook(sb_set_mnt_opts,
-                               opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
-                               opts, kern_flags, set_kern_flags);
+                               mnt_opts ? -EOPNOTSUPP : 0, sb,
+                               mnt_opts, kern_flags, set_kern_flags);
 }
 EXPORT_SYMBOL(security_sb_set_mnt_opts);
 
@@ -449,9 +458,9 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
 }
 EXPORT_SYMBOL(security_sb_clone_mnt_opts);
 
-int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+int security_sb_parse_opts_str(char *options, void **mnt_opts)
 {
-       return call_int_hook(sb_parse_opts_str, 0, options, opts);
+       return call_int_hook(sb_parse_opts_str, 0, options, mnt_opts);
 }
 EXPORT_SYMBOL(security_sb_parse_opts_str);
 
index 11cf2feb27b396a7da90f8e39c6fa69530aa6e9a..caf7ca7abfc16fb04b86060e738f5d060e48aacd 100644 (file)
@@ -433,6 +433,19 @@ static void superblock_free_security(struct super_block *sb)
        kfree(sbsec);
 }
 
+static void selinux_free_mnt_opts(void *mnt_opts)
+{
+       struct security_mnt_opts *opts = mnt_opts;
+       int i;
+
+       if (opts->mnt_opts)
+               for (i = 0; i < opts->num_mnt_opts; i++)
+                       kfree(opts->mnt_opts[i]);
+       kfree(opts->mnt_opts);
+       kfree(opts->mnt_opts_flags);
+       kfree(opts);
+}
+
 static inline int inode_doinit(struct inode *inode)
 {
        return inode_doinit_with_dentry(inode, NULL);
@@ -616,7 +629,7 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
  * labeling information.
  */
 static int selinux_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts,
+                               void *mnt_opts,
                                unsigned long kern_flags,
                                unsigned long *set_kern_flags)
 {
@@ -628,9 +641,10 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        struct inode_security_struct *root_isec;
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
        u32 defcontext_sid = 0;
-       char **mount_options = opts->mnt_opts;
-       int *flags = opts->mnt_opts_flags;
-       int num_opts = opts->num_mnt_opts;
+       struct security_mnt_opts *opts = mnt_opts;
+       char **mount_options = opts ? opts->mnt_opts : NULL;
+       int *flags = opts ? opts->mnt_opts_flags : NULL;
+       int num_opts = opts ? opts->num_mnt_opts : 0;
 
        mutex_lock(&sbsec->lock);
 
@@ -982,12 +996,20 @@ out:
 }
 
 static int selinux_parse_opts_str(char *options,
-                                 struct security_mnt_opts *opts)
+                                 void **mnt_opts)
 {
        char *p;
        char *context = NULL, *defcontext = NULL;
        char *fscontext = NULL, *rootcontext = NULL;
        int rc, num_mnt_opts = 0;
+       struct security_mnt_opts *opts = *mnt_opts;
+
+       if (!opts) {
+               opts = kzalloc(sizeof(struct security_mnt_opts), GFP_KERNEL);
+               *mnt_opts = opts;
+               if (!opts)
+                       return -ENOMEM;
+       }
 
        opts->num_mnt_opts = 0;
 
@@ -1094,7 +1116,7 @@ static int selinux_parse_opts_str(char *options,
        return 0;
 
 out_err:
-       security_free_mnt_opts(opts);
+       security_free_mnt_opts(mnt_opts);
        kfree(context);
        kfree(defcontext);
        kfree(fscontext);
@@ -2714,7 +2736,7 @@ out:
        return rc;
 }
 
-static int selinux_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts)
+static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
 {
        char *s = (char *)get_zeroed_page(GFP_KERNEL);
        int err;
@@ -2723,14 +2745,14 @@ static int selinux_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts
                return -ENOMEM;
        err = selinux_sb_copy_data(options, s);
        if (!err)
-               err = selinux_parse_opts_str(s, opts);
+               err = selinux_parse_opts_str(s, mnt_opts);
        free_page((unsigned long)s);
        return err;
 }
 
-static int selinux_sb_remount(struct super_block *sb,
-                             struct security_mnt_opts *opts)
+static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
 {
+       struct security_mnt_opts *opts = mnt_opts;
        int i, *flags;
        char **mount_options;
        struct superblock_security_struct *sbsec = sb->s_security;
@@ -2738,6 +2760,9 @@ static int selinux_sb_remount(struct super_block *sb,
        if (!(sbsec->flags & SE_SBINITIALIZED))
                return 0;
 
+       if (!opts)
+               return 0;
+
        mount_options = opts->mnt_opts;
        flags = opts->mnt_opts_flags;
 
@@ -6782,6 +6807,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
        LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
        LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
+       LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
        LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
        LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
        LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
@@ -7051,11 +7077,7 @@ static __init int selinux_init(void)
 
 static void delayed_superblock_init(struct super_block *sb, void *unused)
 {
-       struct security_mnt_opts opts;
-
-       security_init_mnt_opts(&opts);
-       selinux_set_mnt_opts(sb, &opts, 0, NULL);
-       security_free_mnt_opts(&opts);
+       selinux_set_mnt_opts(sb, NULL, 0, NULL);
 }
 
 void selinux_complete_init(void)
index 835cca277c2a1c4ec521c7228aaa48ad35e8d1a2..81a8112975d460f824f0e7fb4df4209c56d97436 100644 (file)
@@ -567,6 +567,19 @@ static void smack_sb_free_security(struct super_block *sb)
        sb->s_security = NULL;
 }
 
+static void smack_free_mnt_opts(void *mnt_opts)
+{
+       struct security_mnt_opts *opts = mnt_opts;
+       int i;
+
+       if (opts->mnt_opts)
+               for (i = 0; i < opts->num_mnt_opts; i++)
+                       kfree(opts->mnt_opts[i]);
+       kfree(opts->mnt_opts);
+       kfree(opts->mnt_opts_flags);
+       kfree(opts);
+}
+
 /**
  * smack_sb_copy_data - copy mount options data for processing
  * @orig: where to start
@@ -624,8 +637,9 @@ static int smack_sb_copy_data(char *orig, char *smackopts)
  * converts Smack specific mount options to generic security option format
  */
 static int smack_parse_opts_str(char *options,
-               struct security_mnt_opts *opts)
+               void **mnt_opts)
 {
+       struct security_mnt_opts *opts = *mnt_opts;
        char *p;
        char *fsdefault = NULL;
        char *fsfloor = NULL;
@@ -636,11 +650,17 @@ static int smack_parse_opts_str(char *options,
        int num_mnt_opts = 0;
        int token;
 
-       opts->num_mnt_opts = 0;
-
        if (!options)
                return 0;
 
+       if (!opts) {
+               opts = kzalloc(sizeof(struct security_mnt_opts), GFP_KERNEL);
+               *mnt_opts = opts;
+               if (!opts)
+                       return -ENOMEM;
+       }
+       opts->num_mnt_opts = 0;
+
        while ((p = strsep(&options, ",")) != NULL) {
                substring_t args[MAX_OPT_ARGS];
 
@@ -735,11 +755,11 @@ out_err:
        kfree(fshat);
        kfree(fsroot);
        kfree(fstransmute);
-       security_free_mnt_opts(opts);
+       security_free_mnt_opts(mnt_opts);
        return rc;
 }
 
-static int smack_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts)
+static int smack_sb_eat_lsm_opts(char *options, void **mnt_opts)
 {
        char *s = (char *)get_zeroed_page(GFP_KERNEL);
        int err;
@@ -748,7 +768,7 @@ static int smack_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts)
                return -ENOMEM;
        err = smack_sb_copy_data(options, s);
        if (!err)
-               err = smack_parse_opts_str(s, opts);
+               err = smack_parse_opts_str(s, mnt_opts);
        free_page((unsigned long)s);
        return err;
 }
@@ -766,7 +786,7 @@ static int smack_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts)
  * labels.
  */
 static int smack_set_mnt_opts(struct super_block *sb,
-               struct security_mnt_opts *opts,
+               void *mnt_opts,
                unsigned long kern_flags,
                unsigned long *set_kern_flags)
 {
@@ -776,7 +796,8 @@ static int smack_set_mnt_opts(struct super_block *sb,
        struct inode_smack *isp;
        struct smack_known *skp;
        int i;
-       int num_opts = opts->num_mnt_opts;
+       struct security_mnt_opts *opts = mnt_opts;
+       int num_opts = opts ? opts->num_mnt_opts : 0;
        int transmute = 0;
 
        if (sp->smk_flags & SMK_SB_INITIALIZED)
@@ -4651,6 +4672,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
 
        LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
        LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
+       LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
        LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
        LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
        LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),