[libc-commits] [libc] c59178d - [libc] Fix inet_aton (#198791)

via libc-commits libc-commits at lists.llvm.org
Mon May 25 04:10:07 PDT 2026


Author: Pavel Labath
Date: 2026-05-25T13:10:03+02:00
New Revision: c59178d6037f582c7e8fb0141c7a16cd8d5d8d5e

URL: https://github.com/llvm/llvm-project/commit/c59178d6037f582c7e8fb0141c7a16cd8d5d8d5e
DIFF: https://github.com/llvm/llvm-project/commit/c59178d6037f582c7e8fb0141c7a16cd8d5d8d5e.diff

LOG: [libc] Fix inet_aton (#198791)

The main (in terms of LOC) change is moving the implementation to an
internal function in order to avoid the inet_addr->inet_aton dependency.

I also fix a bug where we (mistakenly) accepted whitespace and signs
inside the address. I also match the glibc implementation in ignoring
the data after the first whitespace.

Added: 
    libc/src/__support/net/CMakeLists.txt
    libc/src/__support/net/address.cpp
    libc/src/__support/net/address.h

Modified: 
    libc/src/__support/CMakeLists.txt
    libc/src/arpa/inet/CMakeLists.txt
    libc/src/arpa/inet/inet_addr.cpp
    libc/src/arpa/inet/inet_aton.cpp
    libc/test/src/arpa/inet/inet_aton_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index ada489046ef9e..1f77fc02380df 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -453,6 +453,7 @@ add_subdirectory(OSUtil)
 add_subdirectory(StringUtil)
 add_subdirectory(GPU)
 add_subdirectory(RPC)
+add_subdirectory(net)
 
 # Thread support is used by other "File". So, we add the "threads"
 # before "File".

diff  --git a/libc/src/__support/net/CMakeLists.txt b/libc/src/__support/net/CMakeLists.txt
new file mode 100644
index 0000000000000..84e9f1bebf230
--- /dev/null
+++ b/libc/src/__support/net/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_object_library(
+  address
+  HDRS
+    address.h
+  SRCS
+    address.cpp
+  DEPENDS
+    libc.hdr.types.in_addr_t
+    libc.src.__support.common
+    libc.src.__support.ctype_utils
+    libc.src.__support.CPP.optional
+    libc.src.__support.str_to_integer
+)

diff  --git a/libc/src/__support/net/address.cpp b/libc/src/__support/net/address.cpp
new file mode 100644
index 0000000000000..1cb0924b07a99
--- /dev/null
+++ b/libc/src/__support/net/address.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements helper functions for parsing network addresses.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/net/address.h"
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/endian_internal.h"
+#include "src/__support/str_to_integer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace net {
+
+cpp::optional<in_addr_t> inet_addr(const char *cp) {
+  constexpr int IPV4_MAX_DOT_NUM = 3;
+  in_addr_t parts[IPV4_MAX_DOT_NUM + 1] = {0};
+  int dot_num = 0;
+
+  for (; dot_num <= IPV4_MAX_DOT_NUM; ++dot_num) {
+    // strtointeger skips leading whitespace signs (1.+2.-3. 4), but we don't
+    // want that, so we explicitly check that the first character is a digit.
+    if (!internal::isdigit(*cp))
+      return cpp::nullopt;
+
+    auto result = internal::strtointeger<in_addr_t>(cp, 0);
+    parts[dot_num] = result;
+
+    if (result.has_error() || result.parsed_len == 0)
+      return cpp::nullopt;
+    cp += result.parsed_len;
+    if (*cp == '\0' || internal::isspace(*cp))
+      break;
+    if (*cp != '.')
+      return cpp::nullopt;
+    ++cp;
+  }
+
+  if (dot_num > IPV4_MAX_DOT_NUM)
+    return cpp::nullopt;
+
+  // converts the Internet host address cp from the IPv4 numbers-and-dots
+  // notation (a[.b[.c[.d]]]) into binary form (in network byte order)
+  in_addr_t result = 0;
+  for (int i = 0; i <= dot_num; ++i) {
+    in_addr_t max_part = i == dot_num ? (0xffffffffu >> (8 * dot_num)) : 0xffu;
+    if (parts[i] > max_part)
+      return cpp::nullopt;
+    int shift = i == dot_num ? 0 : 8 * (IPV4_MAX_DOT_NUM - i);
+    result |= parts[i] << shift;
+  }
+
+  return Endian::to_big_endian(result);
+}
+
+} // namespace net
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/__support/net/address.h b/libc/src/__support/net/address.h
new file mode 100644
index 0000000000000..925b8d379f703
--- /dev/null
+++ b/libc/src/__support/net/address.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains helper functions for parsing and manipulating network
+/// addresses.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
+#define LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
+
+#include "hdr/types/in_addr_t.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace net {
+
+cpp::optional<in_addr_t> inet_addr(const char *cp);
+
+} // namespace net
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H

diff  --git a/libc/src/arpa/inet/CMakeLists.txt b/libc/src/arpa/inet/CMakeLists.txt
index c4019dd4bbd29..da041fc314b43 100644
--- a/libc/src/arpa/inet/CMakeLists.txt
+++ b/libc/src/arpa/inet/CMakeLists.txt
@@ -32,7 +32,8 @@ add_entrypoint_object(
     libc.include.arpa_inet
     libc.hdr.types.struct_in_addr
     libc.src.__support.common
-    libc.src.__support.str_to_integer
+    libc.src.__support.CPP.optional
+    libc.src.__support.net.address
 )
 
 add_entrypoint_object(
@@ -44,10 +45,10 @@ add_entrypoint_object(
   DEPENDS
     libc.include.arpa_inet
     libc.hdr.netinet_in_macros
-    libc.hdr.types.struct_in_addr
     libc.hdr.types.in_addr_t
     libc.src.__support.common
-    libc.src.arpa.inet.inet_aton
+    libc.src.__support.CPP.optional
+    libc.src.__support.net.address
 )
 
 add_entrypoint_object(

diff  --git a/libc/src/arpa/inet/inet_addr.cpp b/libc/src/arpa/inet/inet_addr.cpp
index 03757a1e8fb29..343ccf2057dc5 100644
--- a/libc/src/arpa/inet/inet_addr.cpp
+++ b/libc/src/arpa/inet/inet_addr.cpp
@@ -9,15 +9,17 @@
 #include "src/arpa/inet/inet_addr.h"
 #include "hdr/netinet_in_macros.h"
 #include "hdr/types/in_addr_t.h"
-#include "hdr/types/struct_in_addr.h"
+#include "src/__support/CPP/optional.h"
 #include "src/__support/common.h"
-#include "src/arpa/inet/inet_aton.h"
+#include "src/__support/net/address.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(in_addr_t, inet_addr, (const char *cp)) {
-  struct in_addr addr;
-  return inet_aton(cp, &addr) ? addr.s_addr : INADDR_NONE;
+  cpp::optional<in_addr_t> addr = net::inet_addr(cp);
+  if (!addr.has_value())
+    return INADDR_NONE;
+  return addr.value();
 }
 
 } // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/arpa/inet/inet_aton.cpp b/libc/src/arpa/inet/inet_aton.cpp
index c7f002ec393aa..3dfe9ba994d0a 100644
--- a/libc/src/arpa/inet/inet_aton.cpp
+++ b/libc/src/arpa/inet/inet_aton.cpp
@@ -7,50 +7,18 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/arpa/inet/inet_aton.h"
+#include "src/__support/CPP/optional.h"
 #include "src/__support/common.h"
-#include "src/__support/endian_internal.h"
-#include "src/__support/str_to_integer.h"
+#include "src/__support/net/address.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, inet_aton, (const char *cp, struct in_addr *inp)) {
-  constexpr int IPV4_MAX_DOT_NUM = 3;
-  unsigned long parts[IPV4_MAX_DOT_NUM + 1] = {0};
-  int dot_num = 0;
-
-  for (; dot_num <= IPV4_MAX_DOT_NUM; ++dot_num) {
-    auto result = internal::strtointeger<unsigned long>(cp, 0);
-    parts[dot_num] = result;
-
-    if (result.has_error() || result.parsed_len == 0)
-      return 0;
-    char next_char = *(cp + result.parsed_len);
-    if (next_char != '.' && next_char != '\0')
-      return 0;
-    else if (next_char == '\0')
-      break;
-    else
-      cp += (result.parsed_len + 1);
-  }
-
-  if (dot_num > IPV4_MAX_DOT_NUM)
+  cpp::optional<in_addr_t> addr = net::inet_addr(cp);
+  if (!addr.has_value())
     return 0;
-
-  // converts the Internet host address cp from the IPv4 numbers-and-dots
-  // notation (a[.b[.c[.d]]]) into binary form (in network byte order)
-  unsigned long result = 0;
-  for (int i = 0; i <= dot_num; ++i) {
-    unsigned long max_part =
-        i == dot_num ? (0xffffffffUL >> (8 * dot_num)) : 0xffUL;
-    if (parts[i] > max_part)
-      return 0;
-    int shift = i == dot_num ? 0 : 8 * (IPV4_MAX_DOT_NUM - i);
-    result |= parts[i] << shift;
-  }
-
   if (inp)
-    inp->s_addr = Endian::to_big_endian(static_cast<uint32_t>(result));
-
+    inp->s_addr = addr.value();
   return 1;
 }
 

diff  --git a/libc/test/src/arpa/inet/inet_aton_test.cpp b/libc/test/src/arpa/inet/inet_aton_test.cpp
index 7757ba7ad4d07..dd7ea26374584 100644
--- a/libc/test/src/arpa/inet/inet_aton_test.cpp
+++ b/libc/test/src/arpa/inet/inet_aton_test.cpp
@@ -53,12 +53,30 @@ TEST(LlvmLibcInetAton, ValidTest) {
   a.s_addr = 0;
   ASSERT_EQ(1, inet_aton("036", &a));
   ASSERT_EQ(htonl(036U), a.s_addr);
+
+  // Trailing space.
+  a.s_addr = 0;
+  ASSERT_EQ(1, inet_aton("1 ", &a));
+  ASSERT_EQ(htonl(1), a.s_addr);
+  a.s_addr = 0;
+  ASSERT_EQ(1, inet_aton("1.2 ", &a));
+  ASSERT_EQ(htonl(0x01000002), a.s_addr);
+  a.s_addr = 0;
+  ASSERT_EQ(1, inet_aton("1.2.3 ", &a));
+  ASSERT_EQ(htonl(0x01020003), a.s_addr);
+  a.s_addr = 0;
+  ASSERT_EQ(1, inet_aton("1.2.3.4 ", &a));
+  ASSERT_EQ(htonl(0x01020304), a.s_addr);
+
+  // We can check for validity with a nullptr argument.
+  ASSERT_EQ(1, inet_aton("127.1.2.4", nullptr));
 }
 
 TEST(LlvmLibcInetAton, InvalidTest) {
   ASSERT_EQ(0, inet_aton("", nullptr));           // Empty.
   ASSERT_EQ(0, inet_aton("x", nullptr));          // Leading junk.
   ASSERT_EQ(0, inet_aton("127.0.0.1x", nullptr)); // Trailing junk.
+  ASSERT_EQ(0, inet_aton("127.0.1x", nullptr));   // Trailing junk.
   ASSERT_EQ(0, inet_aton("09.0.0.1", nullptr));   // Invalid octal.
   ASSERT_EQ(0, inet_aton("0xg.0.0.1", nullptr));  // Invalid hex.
   ASSERT_EQ(0, inet_aton("1.2.3.4.5", nullptr));  // Too many dots.
@@ -87,6 +105,13 @@ TEST(LlvmLibcInetAton, InvalidTest) {
 
   // Out of range octal.
   ASSERT_EQ(0, inet_aton("0400.0.0.1", nullptr));
+
+  // Signs and whitespace.
+  ASSERT_EQ(0, inet_aton(" 127.0.0.1", nullptr));
+  ASSERT_EQ(0, inet_aton("+127.0.0.1", nullptr));
+  ASSERT_EQ(0, inet_aton("-127.0.0.1", nullptr));
+  ASSERT_EQ(0, inet_aton("127. 0.0.1", nullptr));
+  ASSERT_EQ(0, inet_aton("127.+0.0.1", nullptr));
 }
 
 } // namespace LIBC_NAMESPACE_DECL


        


More information about the libc-commits mailing list