Fix netd crash in checkUserNetworkAccessLocked()
SIGABRT happens when unprivileged applications call Network.bindSocket
with a local network (netId=99). Netd statically cast a LocalNetwork
object to PhysicalNetwork, call member function getPermission().
However, the function exists in PhysicalNetwork only.
This commit makes getPermission() as a pure virtual function in the
ancestor class and implements it in each subclass.
Bug: 255954808
Test: atest netdclient_root_test
Change-Id: Ibe1eabe6ff4dc4ee124b820d596bb16c3fe371b2
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 5e200e9..4e5a047 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,6 +3,7 @@
{ "name": "netd_integration_test" },
{ "name": "netd_unit_test" },
{ "name": "netdclient_test" },
+ { "name": "netdclient_root_test" },
{ "name": "netdutils_test" }
],
"postsubmit": [
@@ -18,6 +19,7 @@
{ "name": "netd_integration_test" },
{ "name": "netd_unit_test" },
{ "name": "netdclient_test" },
+ { "name": "netdclient_root_test" },
{ "name": "netdutils_test" }
]
}
diff --git a/client/Android.bp b/client/Android.bp
index 22cb5c1..528b354 100644
--- a/client/Android.bp
+++ b/client/Android.bp
@@ -65,3 +65,28 @@
recover: [ "all" ],
},
}
+
+cc_test {
+ name: "netdclient_root_test",
+ require_root: true, // for ScopedUidChange
+ srcs: [
+ "NetdClientRootTest.cpp",
+ ],
+ defaults: [
+ "netd_aidl_interface_lateststable_cpp_static",
+ "netd_defaults",
+ ],
+ test_suites: ["device-tests"],
+ include_dirs: [
+ "system/netd/include",
+ ],
+ static_libs: [
+ "libbase",
+ "libnetd_client",
+ "libnetd_test_utils",
+ ],
+ sanitize: {
+ address: false,
+ recover: [ "all" ],
+ },
+}
diff --git a/client/NetdClientRootTest.cpp b/client/NetdClientRootTest.cpp
new file mode 100644
index 0000000..3ecbcef
--- /dev/null
+++ b/client/NetdClientRootTest.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include "NetdClient.h"
+#include "android/net/INetd.h"
+#include "netid_client.h"
+#include "test_utils.h"
+
+constexpr int TEST_UID1 = 99999;
+
+TEST(NetdClientTest, setSocketToInvalidNetwork) {
+ const android::base::unique_fd s(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ ASSERT_LE(0, s);
+
+ unsigned netId = NETID_UNSET;
+ const ScopedUidChange scopedUidChange(TEST_UID1);
+ EXPECT_EQ(-EACCES, setNetworkForSocket(android::net::INetd::LOCAL_NET_ID, s));
+ EXPECT_EQ(0, getNetworkForSocket(&netId, s));
+ EXPECT_EQ(static_cast<unsigned>(NETID_UNSET), netId);
+}
diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h
index 8f9960b..e699dad 100644
--- a/server/DummyNetwork.h
+++ b/server/DummyNetwork.h
@@ -25,6 +25,7 @@
static const char* INTERFACE_NAME;
explicit DummyNetwork(unsigned netId);
virtual ~DummyNetwork();
+ Permission getPermission() const { return PERMISSION_SYSTEM; };
private:
std::string getTypeString() const override { return "DUMMY"; };
diff --git a/server/LocalNetwork.h b/server/LocalNetwork.h
index c774067..af56d24 100644
--- a/server/LocalNetwork.h
+++ b/server/LocalNetwork.h
@@ -24,6 +24,7 @@
public:
explicit LocalNetwork(unsigned netId);
virtual ~LocalNetwork();
+ Permission getPermission() const { return PERMISSION_SYSTEM; };
private:
std::string getTypeString() const override { return "LOCAL"; };
diff --git a/server/Network.h b/server/Network.h
index dfead17..6c3d01d 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -17,6 +17,7 @@
#pragma once
#include "NetdConstants.h"
+#include "Permission.h"
#include "UidRanges.h"
#include <set>
@@ -48,6 +49,7 @@
std::string toString() const;
std::string uidRangesToString() const;
bool appliesToUser(uid_t uid, int32_t* subPriority) const;
+ virtual Permission getPermission() const = 0;
[[nodiscard]] virtual int addUsers(const UidRanges&, int32_t /*subPriority*/) {
return -EINVAL;
};
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index f144139..ca9ec22 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -887,7 +887,7 @@
// Check whether the UID's permission bits are sufficient to use the network.
// Because the permission of the system default network is PERMISSION_NONE(0x0), apps can always
// pass the check here when using the system default network.
- Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission();
+ const Permission networkPermission = network->getPermission();
return ((userPermission & networkPermission) == networkPermission) ? 0 : -EACCES;
}
diff --git a/server/UnreachableNetwork.h b/server/UnreachableNetwork.h
index d2cefde..0ffc0ce 100644
--- a/server/UnreachableNetwork.h
+++ b/server/UnreachableNetwork.h
@@ -23,6 +23,7 @@
class UnreachableNetwork : public Network {
public:
explicit UnreachableNetwork(unsigned netId);
+ Permission getPermission() const { return PERMISSION_SYSTEM; };
[[nodiscard]] int addUsers(const UidRanges& uidRanges, int32_t subPriority) override;
[[nodiscard]] int removeUsers(const UidRanges& uidRanges, int32_t subPriority) override;
bool isUnreachable() override { return true; }
diff --git a/server/VirtualNetwork.h b/server/VirtualNetwork.h
index 63bc589..6e9bdad 100644
--- a/server/VirtualNetwork.h
+++ b/server/VirtualNetwork.h
@@ -35,6 +35,7 @@
public:
explicit VirtualNetwork(unsigned netId, bool secure, bool excludeLocalRoutes = false);
virtual ~VirtualNetwork();
+ Permission getPermission() const { return PERMISSION_SYSTEM; };
[[nodiscard]] int addUsers(const UidRanges& uidRanges, int32_t subPriority) override;
[[nodiscard]] int removeUsers(const UidRanges& uidRanges, int32_t subPriority) override;
bool isVirtual() override { return true; }