1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 08:19:13 +00:00

Trim abstract unix socket path length

Fixes https://github.com/dart-lang/sdk/issues/46149

TEST=unix_socket_test
Change-Id: Icc5273941f880f581cc2621431b5cecb7782c71e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201441
Commit-Queue: Zach Anderson <zra@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
Zach Anderson 2021-05-27 21:50:24 +00:00 committed by commit-bot@chromium.org
parent f827c53315
commit 5dc1f8db8c
7 changed files with 221 additions and 3 deletions

View File

@ -61,6 +61,10 @@ group("runtime") {
"utils/dds:dds",
]
}
if (is_linux || is_android) {
deps += [ "runtime/bin:abstract_socket_test" ]
}
}
# A separate target and not included in group("runtime"). This way the target\
@ -82,6 +86,9 @@ group("runtime_precompiled") {
"runtime/bin:dart_precompiled_runtime",
"runtime/bin:process_test",
]
if (is_linux || is_android) {
deps += [ "runtime/bin:abstract_socket_test" ]
}
}
group("create_sdk") {

View File

@ -875,6 +875,11 @@ executable("process_test") {
sources = [ "process_test.cc" ]
}
executable("abstract_socket_test") {
sources = [ "abstract_socket_test.cc" ]
include_dirs = [ ".." ]
}
executable("run_vm_tests") {
if (target_os == "fuchsia") {
testonly = true

View File

@ -0,0 +1,104 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This is a utility program for testing that a Dart program can connect to an
// abstract UNIX socket created by a non-Dart program. It creates such a socket
// accepts one connection, echoes back the first message it receives, and then
// closes the connection and UNIX socket.
#include "platform/globals.h"
#if defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
const intptr_t kOffsetOfPtr = 32;
#define OFFSET_OF(type, field) \
(reinterpret_cast<intptr_t>( \
&(reinterpret_cast<type*>(kOffsetOfPtr)->field)) - \
kOffsetOfPtr) // NOLINT
int main(int argc, char* argv[]) {
struct sockaddr_un addr;
char* socket_path;
int server_socket;
char buf[1024];
if (argc < 2) {
fprintf(
stderr,
"Usage: abstract_socket_test <address>\n\n"
"<address> should be an abstract UNIX socket address like @hidden\n");
exit(-1);
}
socket_path = argv[1];
if (socket_path[0] != '@') {
fprintf(stderr,
"The first argument should be an abstract socket "
"address and start with '@'\n");
exit(-1);
}
if ((server_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
addr.sun_path[0] = '\0';
strncpy(addr.sun_path + 1, socket_path + 1, sizeof(addr.sun_path) - 2);
int address_length =
OFFSET_OF(struct sockaddr_un, sun_path) + strlen(socket_path);
if (bind(server_socket, (struct sockaddr*)&addr, address_length) == -1) {
perror("bind error");
exit(-1);
}
if (listen(server_socket, 5) == -1) {
perror("listen error");
exit(-1);
}
int client_socket;
if ((client_socket = accept(server_socket, NULL, NULL)) == -1) {
perror("accept error");
exit(-1);
}
int read_count;
while ((read_count = read(client_socket, buf, sizeof(buf))) > 0) {
int write_count = 0;
while (write_count < read_count) {
int w;
if ((w = write(client_socket, buf, read_count)) < 0) {
perror("write");
exit(-1);
}
write_count += w;
}
}
if (read_count == -1) {
perror("read");
exit(-1);
}
close(client_socket);
close(server_socket);
return 0;
}
#else
int main() {
return -1;
}
#endif // defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)

View File

@ -42,8 +42,26 @@ intptr_t SocketAddress::GetAddrLength(const RawAddr& addr) {
return sizeof(struct sockaddr_in6);
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_UNIX:
return sizeof(struct sockaddr_un);
case AF_UNIX: {
// For an abstract UNIX socket, trailing null bytes in the name are
// meaningful. That is, the bytes '\0/tmp/dbus-xxxx' are a different name
// than '\0/tmp/dbus-xxxx\0\0\0...'. The length of the address structure
// passed to connect() etc. tells those calls how many bytes of the name
// to look at. Therefore, when computing the length of the address in
// this case, any trailing null bytes are trimmed.
// TODO(dart:io): Support abstract UNIX socket addresses that have
// trailing null bytes on purpose.
// https://github.com/dart-lang/sdk/issues/46158
intptr_t nulls = 0;
if (addr.un.sun_path[0] == '\0') {
intptr_t i = sizeof(addr.un.sun_path) - 1;
while (addr.un.sun_path[i] == '\0') {
nulls++;
i--;
}
}
return sizeof(struct sockaddr_un) - nulls;
}
default:
UNREACHABLE();
return 0;

View File

@ -223,7 +223,7 @@ intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr,
intptr_t backlog) {
intptr_t fd = Create(addr);
if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un,
sizeof(struct sockaddr_un))) < 0) {
SocketAddress::GetAddrLength(addr))) < 0) {
FDUtils::SaveErrorAndClose(fd);
return -1;
}

View File

@ -163,6 +163,47 @@ Future testAbstractAddress() async {
await completer.future;
}
String getAbstractSocketTestFileName() {
var executable = Platform.executable;
var dirIndex = executable.lastIndexOf('dart');
var buffer = new StringBuffer(executable.substring(0, dirIndex));
buffer.write('abstract_socket_test');
return buffer.toString();
}
Future testShortAbstractAddress() async {
if (!Platform.isLinux && !Platform.isAndroid) {
return;
}
Process? process;
try {
var socketAddress = '@hidden';
var abstractSocketServer = getAbstractSocketTestFileName();
process = await Process.start(abstractSocketServer, [socketAddress]);
var serverAddress =
InternetAddress(socketAddress, type: InternetAddressType.unix);
Socket client = await Socket.connect(serverAddress, 0);
List<int> sendData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
List<int> data = [];
var completer = Completer<void>();
client.listen(data.addAll, onDone: () {
Expect.listEquals(sendData, data);
completer.complete();
});
client.add(sendData);
await client.close();
await completer.future;
client.destroy();
var exitCode = await process.exitCode;
process = null;
Expect.equals(exitCode, 0);
} catch (e, st) {
Expect.fail('Failed with exception:\n$e\n$st');
} finally {
process?.kill(ProcessSignal.sigkill);
}
}
Future testExistingFile(String name) async {
// Test that a leftover file(In case of previous process being killed and
// finalizer doesn't clean up the file) will be cleaned up and bind() should
@ -398,6 +439,7 @@ void main() async {
await withTempDir('unix_socket_test', (Directory dir) async {
await testHttpServer('${dir.path}');
});
await testShortAbstractAddress();
} catch (e) {
if (Platform.isMacOS || Platform.isLinux || Platform.isAndroid) {
Expect.fail("Unexpected exception $e is thrown");

View File

@ -165,6 +165,47 @@ Future testAbstractAddress() async {
await completer.future;
}
String getAbstractSocketTestFileName() {
var executable = Platform.executable;
var dirIndex = executable.lastIndexOf('dart');
var buffer = new StringBuffer(executable.substring(0, dirIndex));
buffer.write('abstract_socket_test');
return buffer.toString();
}
Future testShortAbstractAddress() async {
if (!Platform.isLinux && !Platform.isAndroid) {
return;
}
Process process;
try {
var socketAddress = '@hidden';
var abstractSocketServer = getAbstractSocketTestFileName();
process = await Process.start(abstractSocketServer, [socketAddress]);
var serverAddress =
InternetAddress(socketAddress, type: InternetAddressType.unix);
Socket client = await Socket.connect(serverAddress, 0);
List<int> sendData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
List<int> data = [];
var completer = Completer<void>();
client.listen(data.addAll, onDone: () {
Expect.listEquals(sendData, data);
completer.complete();
});
client.add(sendData);
await client.close();
await completer.future;
client.destroy();
var exitCode = await process.exitCode;
process = null;
Expect.equals(exitCode, 0);
} catch (e, st) {
Expect.fail('Failed with exception:\n$e\n$st');
} finally {
process?.kill(ProcessSignal.sigkill);
}
}
Future testExistingFile(String name) async {
// Test that a leftover file(In case of previous process being killed and
// finalizer doesn't clean up the file) will be cleaned up and bind() should
@ -400,6 +441,7 @@ void main() async {
await withTempDir('unix_socket_test', (Directory dir) async {
await testHttpServer('${dir.path}');
});
await testShortAbstractAddress();
} catch (e) {
if (Platform.isMacOS || Platform.isLinux || Platform.isAndroid) {
Expect.fail("Unexpected exception $e is thrown");