xfrm/compat: Translate 32-bit user_policy from sockptr
[linux-2.6-microblaze.git] / net / xfrm / xfrm_state.c
index 69520ad..f996188 100644 (file)
@@ -2264,6 +2264,66 @@ static bool km_is_alive(const struct km_event *c)
        return is_alive;
 }
 
+#if IS_ENABLED(CONFIG_XFRM_USER_COMPAT)
+static DEFINE_SPINLOCK(xfrm_translator_lock);
+static struct xfrm_translator __rcu *xfrm_translator;
+
+struct xfrm_translator *xfrm_get_translator(void)
+{
+       struct xfrm_translator *xtr;
+
+       rcu_read_lock();
+       xtr = rcu_dereference(xfrm_translator);
+       if (unlikely(!xtr))
+               goto out;
+       if (!try_module_get(xtr->owner))
+               xtr = NULL;
+out:
+       rcu_read_unlock();
+       return xtr;
+}
+EXPORT_SYMBOL_GPL(xfrm_get_translator);
+
+void xfrm_put_translator(struct xfrm_translator *xtr)
+{
+       module_put(xtr->owner);
+}
+EXPORT_SYMBOL_GPL(xfrm_put_translator);
+
+int xfrm_register_translator(struct xfrm_translator *xtr)
+{
+       int err = 0;
+
+       spin_lock_bh(&xfrm_translator_lock);
+       if (unlikely(xfrm_translator != NULL))
+               err = -EEXIST;
+       else
+               rcu_assign_pointer(xfrm_translator, xtr);
+       spin_unlock_bh(&xfrm_translator_lock);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(xfrm_register_translator);
+
+int xfrm_unregister_translator(struct xfrm_translator *xtr)
+{
+       int err = 0;
+
+       spin_lock_bh(&xfrm_translator_lock);
+       if (likely(xfrm_translator != NULL)) {
+               if (rcu_access_pointer(xfrm_translator) != xtr)
+                       err = -EINVAL;
+               else
+                       RCU_INIT_POINTER(xfrm_translator, NULL);
+       }
+       spin_unlock_bh(&xfrm_translator_lock);
+       synchronize_rcu();
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(xfrm_unregister_translator);
+#endif
+
 int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
 {
        int err;
@@ -2271,9 +2331,6 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
        struct xfrm_mgr *km;
        struct xfrm_policy *pol = NULL;
 
-       if (in_compat_syscall())
-               return -EOPNOTSUPP;
-
        if (sockptr_is_null(optval) && !optlen) {
                xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
                xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
@@ -2288,6 +2345,20 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
        if (IS_ERR(data))
                return PTR_ERR(data);
 
+       if (in_compat_syscall()) {
+               struct xfrm_translator *xtr = xfrm_get_translator();
+
+               if (!xtr)
+                       return -EOPNOTSUPP;
+
+               err = xtr->xlate_user_policy_sockptr(&data, optlen);
+               xfrm_put_translator(xtr);
+               if (err) {
+                       kfree(data);
+                       return err;
+               }
+       }
+
        err = -EINVAL;
        rcu_read_lock();
        list_for_each_entry_rcu(km, &xfrm_km_list, list) {