FirewallController: discover max uid in the current user namespace
This patch gives the capability to FirewallController to discover the
maximum valid uid in the user namespace in which netd is currently
running, and uses that value in the whitelist uid rules.
This is done by parsing the content of /proc/self/uid_map as explained
in the man page of 'user_namespaces'.
On the default root namespace the maximum uid is expected to be
UINT32_MAX - 1, but this assumption is incorrect in other user
namespaces created for instance for container environments.
The uid mapping is de facto constant from within the user namespace and
cannot be modified from inside (more precisely uid_map and gid_map proc
files can only be written once each for a new user namespacE).
netd makes the assumption that the uid mapping stays constant, meaning
it is a bug if the host namespace tries to remap uids after netd starts.
Bug: 110459356
Test: - built,
- flashed and booted a marlin, 'fw_powersave' rule is as expected
- flashed and booted ARC++ container, 'fw_powersave' rule is as
expected
- new unit tests pass
Change-Id: I44a885c34e174b0067848b860be8d7b8f3e83296
diff --git a/server/FirewallControllerTest.cpp b/server/FirewallControllerTest.cpp
index 952b21e..240c85d 100644
--- a/server/FirewallControllerTest.cpp
+++ b/server/FirewallControllerTest.cpp
@@ -22,14 +22,16 @@
#include <gtest/gtest.h>
-#include <android-base/strings.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "FirewallController.h"
#include "IptablesBaseTest.h"
using android::base::Join;
using android::base::StringPrintf;
+using android::base::WriteStringToFile;
class FirewallControllerTest : public IptablesBaseTest {
protected:
@@ -295,3 +297,50 @@
EXPECT_EQ(0, mFw.enableFirewall(WHITELIST));
expectIptablesRestoreCommands(noCommands);
}
+
+TEST_F(FirewallControllerTest, TestDiscoverMaximumValidUid) {
+ struct {
+ const std::string description;
+ const std::string content;
+ const uint32_t expected;
+ } testCases[] = {
+ {
+ .description = "root namespace case",
+ .content = " 0 0 4294967295",
+ .expected = 4294967294,
+ },
+ {
+ .description = "container namespace case",
+ .content = " 0 655360 5000\n"
+ " 5000 600 50\n"
+ " 5050 660410 1994950\n",
+ .expected = 1999999,
+ },
+ {
+ .description = "garbage content case",
+ .content = "garbage",
+ .expected = 4294967294,
+ },
+ {
+ .description = "no content case",
+ .content = "",
+ .expected = 4294967294,
+ },
+ };
+
+ const std::string tempFile = "/data/local/tmp/fake_uid_mapping";
+
+ for (const auto& test : testCases) {
+ EXPECT_TRUE(WriteStringToFile(test.content, tempFile, false));
+ uint32_t got = FirewallController::discoverMaximumValidUid(tempFile);
+ EXPECT_EQ(0, remove(tempFile.c_str()));
+ if (got != test.expected) {
+ FAIL() << test.description << ":\n"
+ << test.content << "\ngot " << got << ", but expected " << test.expected;
+ }
+ }
+
+ // Also check when the file is not defined
+ EXPECT_NE(0, access(tempFile.c_str(), F_OK));
+ EXPECT_EQ(4294967294, FirewallController::discoverMaximumValidUid(tempFile));
+}