From 57c633981dd80f602f829434275b93103d6251c7 Mon Sep 17 00:00:00 2001
From: bwzhang <zhangbowei@kylinos.cn>
Date: Mon, 18 Mar 2024 09:48:34 +0800
Subject: [PATCH] fix CVE-2022-0811

---
 internal/config/nsmgr/nsmgr.go | 23 ++--------------------
 pinns/src/pinns.c              | 10 +++++++---
 pinns/src/sysctl.c             | 35 +++++++++-------------------------
 pinns/src/sysctl.h             |  2 +-
 4 files changed, 19 insertions(+), 51 deletions(-)

diff --git a/internal/config/nsmgr/nsmgr.go b/internal/config/nsmgr/nsmgr.go
index 6c7d8ce..344419b 100644
--- a/internal/config/nsmgr/nsmgr.go
+++ b/internal/config/nsmgr/nsmgr.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"strings"
 	"syscall"
 
 	nspkg "github.com/containernetworking/plugins/pkg/ns"
@@ -87,12 +86,8 @@ func (mgr *NamespaceManager) NewPodNamespaces(cfg *PodNamespacesConfig) ([]Names
 		"-f", pinnedNamespace,
 	}
 
-	if len(cfg.Sysctls) != 0 {
-		pinnsSysctls, err := getSysctlForPinns(cfg.Sysctls)
-		if err != nil {
-			return nil, errors.Wrapf(err, "invalid sysctl")
-		}
-		pinnsArgs = append(pinnsArgs, "-s", pinnsSysctls)
+	for key, value := range cfg.Sysctls {
+		pinnsArgs = append(pinnsArgs, "-s", fmt.Sprintf("%s=%s", key, value))
 	}
 
 	var rootPair idtools.IDPair
@@ -175,20 +170,6 @@ func getMappingsForPinns(mappings []idtools.IDMap) string {
 	return g.String()
 }
 
-func getSysctlForPinns(sysctls map[string]string) (string, error) {
-	// This assumes there's no valid sysctl value with a `+` in it
-	// and as such errors if one is found.
-	const pinnsSysctlDelim = "+"
-	g := new(bytes.Buffer)
-	for key, value := range sysctls {
-		if strings.Contains(key, pinnsSysctlDelim) || strings.Contains(value, pinnsSysctlDelim) {
-			return "", errors.Errorf("'%s=%s' is invalid: %s found yet should not be present", key, value, pinnsSysctlDelim)
-		}
-		fmt.Fprintf(g, "'%s=%s'%s", key, value, pinnsSysctlDelim)
-	}
-	return strings.TrimSuffix(g.String(), pinnsSysctlDelim), nil
-}
-
 // NamespaceFromProcEntry creates a new namespace object from a bind mount from a processes proc entry.
 // The caller is responsible for cleaning up the namespace by calling Namespace.Remove().
 // This function is heavily based on containernetworking ns package found at:
diff --git a/pinns/src/pinns.c b/pinns/src/pinns.c
index dc25d09..f44df47 100644
--- a/pinns/src/pinns.c
+++ b/pinns/src/pinns.c
@@ -49,7 +49,8 @@ int main(int argc, char **argv) {
   bool bind_user = false;
   bool bind_cgroup = false;
   bool bind_mount = false;
-  char *sysctls = NULL;
+  char **sysctls = NULL;
+  int sysctls_count = 0;
   char res;
 
   static const struct option long_options[] = {
@@ -67,6 +68,8 @@ int main(int argc, char **argv) {
       {"sysctl", optional_argument, NULL, 's'},
   };
 
+  sysctls = calloc(argc/2, sizeof(char *));
+
   while ((c = getopt_long(argc, argv, "mpchuUind:f:s:", long_options, NULL)) != -1) {
     switch (c) {
     case 'u':
@@ -113,7 +116,8 @@ int main(int argc, char **argv) {
       pin_path = optarg;
       break;
     case 's':
-	  sysctls = optarg;
+      sysctls[sysctls_count] = optarg;
+      sysctls_count++;
       break;
     case 'f':
       filename = optarg;
@@ -239,7 +243,7 @@ int main(int argc, char **argv) {
     close(p[0]);
   }
 
-  if (sysctls && configure_sysctls(sysctls) < 0) {
+  if (sysctls_count != 0 && configure_sysctls(sysctls, sysctls_count) < 0) {
     pexit("Failed to configure sysctls after unshare");
   }
 
diff --git a/pinns/src/sysctl.c b/pinns/src/sysctl.c
index 058afb9..1c7377c 100644
--- a/pinns/src/sysctl.c
+++ b/pinns/src/sysctl.c
@@ -8,37 +8,27 @@
 
 static int separate_sysctl_key_value (char* sysctl_key_value, char** sysctl_key, char** sysctl_value);
 static int write_sysctl_to_file (char * sysctl_key, char* sysctl_value);
-const char *sysctl_delim = "+";
 
-int configure_sysctls (char * const sysctls)
+int configure_sysctls (char ** const sysctls, int size)
 {
-  char* sysctl = strtok(sysctls, sysctl_delim);
   char* key = NULL;
   char* value = NULL;
-  while (sysctl)
+
+  for (int i = 0; i < size; ++i)
   {
-    if (separate_sysctl_key_value (sysctl, &key, &value) < 0)
+    if (separate_sysctl_key_value (sysctls[i], &key, &value) < 0)
       return -1;
 
     if (write_sysctl_to_file (key, value) < 0)
       return -1;
-    sysctl = strtok (NULL, sysctl_delim);
   }
 
   return 0;
 }
 
-// key_value should be in the form `'k=v'`
+// key_value should be in the form `k=v`
 static int separate_sysctl_key_value (char* key_value, char** key, char** value)
 {
-  // begin by stripping the `'`, we now have `k=v'`
-  bool quote_stripped = false;
-  if (*key_value == '\'')
-  {
-    key_value++;
-    quote_stripped = true;
-  }
-
   // now find the `=` and convert it to a delimiter
   char * equals_token = strchr (key_value, '=');
   if (!equals_token)
@@ -55,31 +45,24 @@ static int separate_sysctl_key_value (char* key_value, char** key, char** value)
     return -1;
   }
 
-  // we now have `k\0v'`
+  // we now have `k\0v`
   *equals_token = '\0';
 
   // key is now `k`
   *key = key_value;
 
-  // equals_token is now `v'`
+  // equals_token is now `v`
   ++equals_token;
 
-  // if we stripped the beginning single quote
-  // we should find and strip the ending, as well as anything after
-  if (quote_stripped)
-  {
-    char* ending_char = strchr (equals_token, '\'');
-    if (ending_char)
-      *ending_char = '\0';
-  }
-
   // value is now `v`
   *value = equals_token;
+
   if (!strlen (*value))
   {
     nwarnf ("sysctl must be in the form of 'key=value'; value is empty");
     return -1;
   }
+
   return 0;
 }
 
diff --git a/pinns/src/sysctl.h b/pinns/src/sysctl.h
index 9f90d4e..2a753eb 100644
--- a/pinns/src/sysctl.h
+++ b/pinns/src/sysctl.h
@@ -2,6 +2,6 @@
 #if !defined(SYSCTL_H)
 #define SYSCTL_H
 
-int configure_sysctls (char * const sysctls);
+int configure_sysctls (char ** const sysctls, int size);
 
 #endif // SYSCTL_H
-- 
2.20.1

