mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 18:47:55 +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:
parent
f827c53315
commit
5dc1f8db8c
7
BUILD.gn
7
BUILD.gn
|
@ -61,6 +61,10 @@ group("runtime") {
|
||||||
"utils/dds:dds",
|
"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\
|
# 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:dart_precompiled_runtime",
|
||||||
"runtime/bin:process_test",
|
"runtime/bin:process_test",
|
||||||
]
|
]
|
||||||
|
if (is_linux || is_android) {
|
||||||
|
deps += [ "runtime/bin:abstract_socket_test" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group("create_sdk") {
|
group("create_sdk") {
|
||||||
|
|
|
@ -875,6 +875,11 @@ executable("process_test") {
|
||||||
sources = [ "process_test.cc" ]
|
sources = [ "process_test.cc" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executable("abstract_socket_test") {
|
||||||
|
sources = [ "abstract_socket_test.cc" ]
|
||||||
|
include_dirs = [ ".." ]
|
||||||
|
}
|
||||||
|
|
||||||
executable("run_vm_tests") {
|
executable("run_vm_tests") {
|
||||||
if (target_os == "fuchsia") {
|
if (target_os == "fuchsia") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
104
runtime/bin/abstract_socket_test.cc
Normal file
104
runtime/bin/abstract_socket_test.cc
Normal 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)
|
|
@ -42,8 +42,26 @@ intptr_t SocketAddress::GetAddrLength(const RawAddr& addr) {
|
||||||
return sizeof(struct sockaddr_in6);
|
return sizeof(struct sockaddr_in6);
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return sizeof(struct sockaddr_in);
|
return sizeof(struct sockaddr_in);
|
||||||
case AF_UNIX:
|
case AF_UNIX: {
|
||||||
return sizeof(struct sockaddr_un);
|
// 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:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -223,7 +223,7 @@ intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr,
|
||||||
intptr_t backlog) {
|
intptr_t backlog) {
|
||||||
intptr_t fd = Create(addr);
|
intptr_t fd = Create(addr);
|
||||||
if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un,
|
if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un,
|
||||||
sizeof(struct sockaddr_un))) < 0) {
|
SocketAddress::GetAddrLength(addr))) < 0) {
|
||||||
FDUtils::SaveErrorAndClose(fd);
|
FDUtils::SaveErrorAndClose(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,6 +163,47 @@ Future testAbstractAddress() async {
|
||||||
await completer.future;
|
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 {
|
Future testExistingFile(String name) async {
|
||||||
// Test that a leftover file(In case of previous process being killed and
|
// 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
|
// 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 withTempDir('unix_socket_test', (Directory dir) async {
|
||||||
await testHttpServer('${dir.path}');
|
await testHttpServer('${dir.path}');
|
||||||
});
|
});
|
||||||
|
await testShortAbstractAddress();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (Platform.isMacOS || Platform.isLinux || Platform.isAndroid) {
|
if (Platform.isMacOS || Platform.isLinux || Platform.isAndroid) {
|
||||||
Expect.fail("Unexpected exception $e is thrown");
|
Expect.fail("Unexpected exception $e is thrown");
|
||||||
|
|
|
@ -165,6 +165,47 @@ Future testAbstractAddress() async {
|
||||||
await completer.future;
|
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 {
|
Future testExistingFile(String name) async {
|
||||||
// Test that a leftover file(In case of previous process being killed and
|
// 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
|
// 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 withTempDir('unix_socket_test', (Directory dir) async {
|
||||||
await testHttpServer('${dir.path}');
|
await testHttpServer('${dir.path}');
|
||||||
});
|
});
|
||||||
|
await testShortAbstractAddress();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (Platform.isMacOS || Platform.isLinux || Platform.isAndroid) {
|
if (Platform.isMacOS || Platform.isLinux || Platform.isAndroid) {
|
||||||
Expect.fail("Unexpected exception $e is thrown");
|
Expect.fail("Unexpected exception $e is thrown");
|
||||||
|
|
Loading…
Reference in a new issue