Message ID | 160ddef0313e11085ee906144d6d9678b8156171.1690307717.git.tanyuan@tinylab.org |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp2661640vqg; Tue, 25 Jul 2023 11:36:25 -0700 (PDT) X-Google-Smtp-Source: APBJJlEY14flSc9cAV9KUvLm+R8V8WH6ty5eE0TnUrLYi9b6PxSrE7u40dKe8tN8QY37RrQ6Zcji X-Received: by 2002:a05:6a00:418a:b0:65e:1d92:c0cc with SMTP id ca10-20020a056a00418a00b0065e1d92c0ccmr4082922pfb.10.1690310184847; Tue, 25 Jul 2023 11:36:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690310184; cv=none; d=google.com; s=arc-20160816; b=L4sHHJozNBTNLquY86RHbzg3rd4tVWxLjW+YStR63UxS7qDZMIZn1TUdRBDUl4B62r EvYwpo9MrS0+M8EIr9/SDZzK4PhgxIk7CUfePE5Ue7VZwp53vqxGIAKO0vZLlXPkM07W IbxzC+8t6OT0zH9NTZWd5YtiaPHBFrACKZDEmqNYXW2BhnQ/g9wFR0bnHunDifDTrGta 8wHeL5G2k436kVGuGVNBhX6m+UqjpkCoVlwuxSP0wJFXht4Miw5I7NFkdR7MMJUDgSww svHAozl/Jmwix1qXUd4BorYp5YTW6vv9UQh0LH+Wd6uXH1ap7pORpntji6twN8WZ3v6y J7hA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:feedback-id:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from; bh=omGdsiG5n/VsSPxcL2MTsWdQc1v/QheXREnLNOI+PvM=; fh=4Cpblm3wSM4rkkZEwabgTF4Z1dGAqmqNRU4fr+t5W/s=; b=PPfs6I5XSp27O9JbuNAY0PeSNgx2x6kb/2ig0vtJbS82EJO5ychmcRou9La6ljxvjN zr7JC/nksCNhjCFa3O5/ojmDrbNNr9ljvumOc4wpcyTFez5FRZdmOlOAPlaMCnqnkK05 PPn0HWj8o91ev6s/9jqNUAUfjdaLrrxj+sHLYji4CQaG+dazdloETlVlpbYllTNlK9qP 3OIaU3QIWyRKhaSa9fQJb+qk+AvRMO23l5IDC3SrtlveYICbhScWZLbZdWxq3ikt3jSE HeK5yqbIc+j9ywHFF76jkCKL8neC8tjGD+tsFobUY7vOKAQUiVcixuQakZJ7M9E9SxIi 46qA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id y2-20020a056a001c8200b00682850547a9si11225683pfw.201.2023.07.25.11.36.11; Tue, 25 Jul 2023 11:36:24 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232571AbjGYSBv (ORCPT <rfc822;kautuk.consul.80@gmail.com> + 99 others); Tue, 25 Jul 2023 14:01:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53218 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232383AbjGYSBl (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Tue, 25 Jul 2023 14:01:41 -0400 Received: from bg4.exmail.qq.com (bg4.exmail.qq.com [43.154.54.12]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D7FCA2115; Tue, 25 Jul 2023 11:01:38 -0700 (PDT) X-QQ-mid: bizesmtp62t1690308093t9wenc48 Received: from localhost.localdomain ( [42.242.128.198]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 26 Jul 2023 02:01:32 +0800 (CST) X-QQ-SSF: 01200000000000403000B00A0000000 X-QQ-FEAT: hoArX50alxGfKCqcoJrDpBOVNpOBAj8Euz2UJh6j3yf0pnrWIamc1qk6NJsZy ZPdG6F8iyNMtFTdqKUKL/Y80gyczqCycfftYBrPWMlKrVYIJUsBXBCwq4dUDN9P/gRGQ6MX 5v25aVNj8TMCLPo2SaYhIll5Zlpo/tzxtqGCtW+EvmEfl+bowYQeHhK06ZkQGRojxoMsHfg MqDHWHmeJCKE0avHl7btJ2zcdw0ZpQYhMGYIfLkonv5xzILyLqC5SGFCNe5IWZOyxn6z4Po ldkvveqgISSqk/h/KcsH8ZmcYKQNEg/GrmvqFlrnIlsXP1TvwXRssxHReSNcT3OpPUS3rSb WI54hW64St4bxdwyT/GZd0Tt13b2WiJypbOQ+ol1nT5mO47I77QYh81YNn2RQ== X-QQ-GoodBg: 0 X-BIZMAIL-ID: 4769715897786675155 From: Yuan Tan <tanyuan@tinylab.org> To: w@1wt.eu Cc: falcon@tinylab.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Yuan Tan <tanyuan@tinylab.org> Subject: [PATCH 2/2] selftests/nolibc: add testcase for pipe. Date: Tue, 25 Jul 2023 14:01:30 -0400 Message-Id: <160ddef0313e11085ee906144d6d9678b8156171.1690307717.git.tanyuan@tinylab.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <cover.1690307717.git.tanyuan@tinylab.org> References: <cover.1690307717.git.tanyuan@tinylab.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-QQ-SENDSIZE: 520 Feedback-ID: bizesmtp:tinylab.org:qybglogicsvrgz:qybglogicsvrgz5a-1 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772418692001182194 X-GMAIL-MSGID: 1772418692001182194 |
Series |
tools/nolibc: add pipe() and its testcase
|
|
Commit Message
Yuan Tan
July 25, 2023, 6:01 p.m. UTC
Add a testcase of pipe that child process sends message to parent process.
Signed-off-by: Yuan Tan <tanyuan@tinylab.org>
---
tools/testing/selftests/nolibc/nolibc-test.c | 34 ++++++++++++++++++++
1 file changed, 34 insertions(+)
Comments
On 2023-07-25 14:01:30-0400, Yuan Tan wrote: > Add a testcase of pipe that child process sends message to parent process. > > Signed-off-by: Yuan Tan <tanyuan@tinylab.org> > --- > tools/testing/selftests/nolibc/nolibc-test.c | 34 ++++++++++++++++++++ > 1 file changed, 34 insertions(+) > > diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c > index 03b1d30f5507..43ba2884fd1e 100644 > --- a/tools/testing/selftests/nolibc/nolibc-test.c > +++ b/tools/testing/selftests/nolibc/nolibc-test.c > @@ -767,6 +767,39 @@ int test_mmap_munmap(void) > return ret; > } > > +int test_pipe(void) > +{ > + int pipefd[2]; > + char buf[32]; > + pid_t pid; > + char *msg = "hello, nolibc"; const char * const > + > + if (pipe(pipefd) == -1) > + return 1; > + > + pid = fork(); > + > + switch (pid) { > + case -1: > + return 1; > + > + case 0: > + close(pipefd[0]); > + write(pipefd[1], msg, strlen(msg)); Isn't this missing to write trailing the 0 byte? Also check the return value. > + close(pipefd[1]); Do we need to close the pipefds? The process is exiting anyways. > + exit(EXIT_SUCCESS); > + > + default: > + close(pipefd[1]); > + read(pipefd[0], buf, 32); Use sizeof(buf). Check return value == strlen(msg). > + close(pipefd[0]); > + wait(NULL); waitpid(pid, NULL, 0); > + > + if (strcmp(buf, msg)) > + return 1; > + return 0; return !!strcmp(buf, msg); > + } > +} > > /* Run syscall tests between IDs <min> and <max>. > * Return 0 on success, non-zero on failure. > @@ -851,6 +884,7 @@ int run_syscall(int min, int max) > CASE_TEST(mmap_munmap_good); EXPECT_SYSZR(1, test_mmap_munmap()); break; > CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break; > CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break; > + CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; > CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; > CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break; > CASE_TEST(poll_fault); EXPECT_SYSER(1, poll((void *)1, 1, 0), -1, EFAULT); break; > -- > 2.39.2 >
On Sun, Jul 30, 2023 at 12:17:24AM +0200, Thomas Weißschuh wrote: > > + case 0: > > + close(pipefd[0]); > > + write(pipefd[1], msg, strlen(msg)); > > Isn't this missing to write trailing the 0 byte? It depends if the other side expects to get the trailing 0. In general it's better to avoid sending it since it's only used for internal representation, and the other side must be prepared to receive anything anyway. > Also check the return value. Indeed! > > + close(pipefd[1]); > > Do we need to close the pipefds? The process is exiting anyways. It's better to, because we could imagine looping over the tests for example. Thus each test shoulld have as little impact as possible on other tests. > > + exit(EXIT_SUCCESS); > > + > > + default: > > + close(pipefd[1]); > > + read(pipefd[0], buf, 32); > > Use sizeof(buf). Check return value == strlen(msg). > > > + close(pipefd[0]); > > + wait(NULL); > > waitpid(pid, NULL, 0); > > > + > > + if (strcmp(buf, msg)) > > + return 1; > > + return 0; > > return !!strcmp(buf, msg); In fact before that we need to terminate the output buffer. If for any reason the transfer fails (e.g. the syscall fails or transfers data at another location or of another length, we could end up comparing past the end of the buffer. Thus I suggest adding this immediately after the read(): buf[sizeof(buf) - 1] = 0; Willy
On 2023-07-30 05:33:43+0200, Willy Tarreau wrote: > On Sun, Jul 30, 2023 at 12:17:24AM +0200, Thomas Weißschuh wrote: > > > + case 0: > > > + close(pipefd[0]); > > > + write(pipefd[1], msg, strlen(msg)); > > > > Isn't this missing to write trailing the 0 byte? > > It depends if the other side expects to get the trailing 0. > In general it's better to avoid sending it since it's only > used for internal representation, and the other side must > be prepared to receive anything anyway. > > > Also check the return value. > > Indeed! > > > > + close(pipefd[1]); > > > > Do we need to close the pipefds? The process is exiting anyways. > > It's better to, because we could imagine looping over the tests for > example. Thus each test shoulld have as little impact as possible > on other tests. I meant the newly forked child exiting, not nolibc-test in general. The exit is just below, so the fds in the child are close here anyways. | | v > > > + exit(EXIT_SUCCESS); > > > + > > > + default: > > > + close(pipefd[1]); > > > + read(pipefd[0], buf, 32); > > > > Use sizeof(buf). Check return value == strlen(msg). > > > > > + close(pipefd[0]); > > > + wait(NULL); > > > > waitpid(pid, NULL, 0); > > > > > + > > > + if (strcmp(buf, msg)) > > > + return 1; > > > + return 0; > > > > return !!strcmp(buf, msg); > > In fact before that we need to terminate the output buffer. If for any > reason the transfer fails (e.g. the syscall fails or transfers data at > another location or of another length, we could end up comparing past > the end of the buffer. Thus I suggest adding this immediately after the > read(): > > buf[sizeof(buf) - 1] = 0; This would still access uninitialized memory and lead to UB in strcmp as not all bytes in buf were written to by read(). If we want to be really sure we should use memcmp() instead of strcmp(). For memcmp() I would prefer to transfer and check without the '\0', so my review comments from before need to be adapted a bit.
On Sun, Jul 30, 2023 at 08:55:47AM +0200, Thomas Weißschuh wrote: > On 2023-07-30 05:33:43+0200, Willy Tarreau wrote: > > On Sun, Jul 30, 2023 at 12:17:24AM +0200, Thomas Weißschuh wrote: > > > > + case 0: > > > > + close(pipefd[0]); > > > > + write(pipefd[1], msg, strlen(msg)); > > > > > > Isn't this missing to write trailing the 0 byte? > > > > It depends if the other side expects to get the trailing 0. > > In general it's better to avoid sending it since it's only > > used for internal representation, and the other side must > > be prepared to receive anything anyway. > > > > > Also check the return value. > > > > Indeed! > > > > > > + close(pipefd[1]); > > > > > > Do we need to close the pipefds? The process is exiting anyways. > > > > It's better to, because we could imagine looping over the tests for > > example. Thus each test shoulld have as little impact as possible > > on other tests. > > I meant the newly forked child exiting, not nolibc-test in general. > The exit is just below, so the fds in the child are close here anyways. Ah OK, but still it remains cleaner with it IMHO (i.e. better rely on explicit things in tests, that's less doubts when they fail). > > > > + default: > > > > + close(pipefd[1]); > > > > + read(pipefd[0], buf, 32); > > > > > > Use sizeof(buf). Check return value == strlen(msg). > > > > > > > + close(pipefd[0]); > > > > + wait(NULL); > > > > > > waitpid(pid, NULL, 0); > > > > > > > + > > > > + if (strcmp(buf, msg)) > > > > + return 1; > > > > + return 0; > > > > > > return !!strcmp(buf, msg); > > > > In fact before that we need to terminate the output buffer. If for any > > reason the transfer fails (e.g. the syscall fails or transfers data at > > another location or of another length, we could end up comparing past > > the end of the buffer. Thus I suggest adding this immediately after the > > read(): > > > > buf[sizeof(buf) - 1] = 0; > > This would still access uninitialized memory and lead to UB in strcmp as > not all bytes in buf were written to by read(). > > If we want to be really sure we should use memcmp() instead of strcmp(). > For memcmp() I would prefer to transfer and check without the '\0', so > my review comments from before need to be adapted a bit. In fact you make a good point regarding the fact that the test doesn't use read()'s return value. This problem totally goes away if the return value is used, e.g.: len = read(pipefd[0], buf, sizeof(buf)); close(pipefd[0]); waitpid(pid, NULL, 0); return len < 0 || len > sizeof(buf) || len > strlen(msg) || memcmp(buf, msg, len) != 0; Willy
On 2023-07-30 09:12:27+0200, Willy Tarreau wrote: > On Sun, Jul 30, 2023 at 08:55:47AM +0200, Thomas Weißschuh wrote: > > On 2023-07-30 05:33:43+0200, Willy Tarreau wrote: > > > On Sun, Jul 30, 2023 at 12:17:24AM +0200, Thomas Weißschuh wrote: > > > > > + case 0: > > > > > + close(pipefd[0]); > > > > > + write(pipefd[1], msg, strlen(msg)); > > > > > > > > Isn't this missing to write trailing the 0 byte? > > > > > > It depends if the other side expects to get the trailing 0. > > > In general it's better to avoid sending it since it's only > > > used for internal representation, and the other side must > > > be prepared to receive anything anyway. > > > > > > > Also check the return value. > > > > > > Indeed! > > > > > > > > + close(pipefd[1]); > > > > > > > > Do we need to close the pipefds? The process is exiting anyways. > > > > > > It's better to, because we could imagine looping over the tests for > > > example. Thus each test shoulld have as little impact as possible > > > on other tests. > > > > I meant the newly forked child exiting, not nolibc-test in general. > > The exit is just below, so the fds in the child are close here anyways. > > Ah OK, but still it remains cleaner with it IMHO (i.e. better rely on > explicit things in tests, that's less doubts when they fail). Accepted :-) > > > > > + default: > > > > > + close(pipefd[1]); > > > > > + read(pipefd[0], buf, 32); > > > > > > > > Use sizeof(buf). Check return value == strlen(msg). > > > > > > > > > + close(pipefd[0]); > > > > > + wait(NULL); > > > > > > > > waitpid(pid, NULL, 0); > > > > > > > > > + > > > > > + if (strcmp(buf, msg)) > > > > > + return 1; > > > > > + return 0; > > > > > > > > return !!strcmp(buf, msg); > > > > > > In fact before that we need to terminate the output buffer. If for any > > > reason the transfer fails (e.g. the syscall fails or transfers data at > > > another location or of another length, we could end up comparing past > > > the end of the buffer. Thus I suggest adding this immediately after the > > > read(): > > > > > > buf[sizeof(buf) - 1] = 0; > > > > This would still access uninitialized memory and lead to UB in strcmp as > > not all bytes in buf were written to by read(). > > > > If we want to be really sure we should use memcmp() instead of strcmp(). > > For memcmp() I would prefer to transfer and check without the '\0', so > > my review comments from before need to be adapted a bit. > > In fact you make a good point regarding the fact that the test doesn't > use read()'s return value. This problem totally goes away if the return > value is used, e.g.: > > len = read(pipefd[0], buf, sizeof(buf)); > close(pipefd[0]); > waitpid(pid, NULL, 0); > return len < 0 || len > sizeof(buf) || len > strlen(msg) || memcmp(buf, msg, len) != 0; Wouldn't this happily accept len == 0? Why not just: if (len != strlen(msg)) return 1; return !!memcmp(buf, msg, len); Also so far we have assumed that one call one call to read() is enough. But looking at pipe(7) this is not guaranteed by the spec. If we want to be really sure, a loop around read() seems to be necessary.
On Sun, Jul 30, 2023 at 10:07:24AM +0200, Thomas Weißschuh wrote: > > In fact you make a good point regarding the fact that the test doesn't > > use read()'s return value. This problem totally goes away if the return > > value is used, e.g.: > > > > len = read(pipefd[0], buf, sizeof(buf)); > > close(pipefd[0]); > > waitpid(pid, NULL, 0); > > return len < 0 || len > sizeof(buf) || len > strlen(msg) || memcmp(buf, msg, len) != 0; > > Wouldn't this happily accept len == 0? > > Why not just: > > if (len != strlen(msg)) > return 1; > return !!memcmp(buf, msg, len); Indeed, works for me. > Also so far we have assumed that one call one call to read() is enough. > But looking at pipe(7) this is not guaranteed by the spec. > If we want to be really sure, a loop around read() seems to be necessary. In practice it will be OK as the message is small and sent in one syscall, so let's not care too much about this for now. Willy
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 03b1d30f5507..43ba2884fd1e 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -767,6 +767,39 @@ int test_mmap_munmap(void) return ret; } +int test_pipe(void) +{ + int pipefd[2]; + char buf[32]; + pid_t pid; + char *msg = "hello, nolibc"; + + if (pipe(pipefd) == -1) + return 1; + + pid = fork(); + + switch (pid) { + case -1: + return 1; + + case 0: + close(pipefd[0]); + write(pipefd[1], msg, strlen(msg)); + close(pipefd[1]); + exit(EXIT_SUCCESS); + + default: + close(pipefd[1]); + read(pipefd[0], buf, 32); + close(pipefd[0]); + wait(NULL); + + if (strcmp(buf, msg)) + return 1; + return 0; + } +} /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. @@ -851,6 +884,7 @@ int run_syscall(int min, int max) CASE_TEST(mmap_munmap_good); EXPECT_SYSZR(1, test_mmap_munmap()); break; CASE_TEST(open_tty); EXPECT_SYSNE(1, tmp = open("/dev/null", 0), -1); if (tmp != -1) close(tmp); break; CASE_TEST(open_blah); EXPECT_SYSER(1, tmp = open("/proc/self/blah", 0), -1, ENOENT); if (tmp != -1) close(tmp); break; + CASE_TEST(pipe); EXPECT_SYSZR(1, test_pipe()); break; CASE_TEST(poll_null); EXPECT_SYSZR(1, poll(NULL, 0, 0)); break; CASE_TEST(poll_stdout); EXPECT_SYSNE(1, ({ struct pollfd fds = { 1, POLLOUT, 0}; poll(&fds, 1, 0); }), -1); break; CASE_TEST(poll_fault); EXPECT_SYSER(1, poll((void *)1, 1, 0), -1, EFAULT); break;