From patchwork Thu Dec 19 03:17:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 103405 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E1E293858CD9 for ; Thu, 19 Dec 2024 03:18:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E1E293858CD9 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Diq1Dghw X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 443873858D20 for ; Thu, 19 Dec 2024 03:17:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 443873858D20 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 443873858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1734578277; cv=none; b=bKQWy4OCWwCuz8+UTsQdz6Yyis+7y33L9gc9JepIaUkMhqIkAD863s3E9lYyYXPBb59NsSA+ihWckTPMRXcuytBSXAP5su9MEheqdl9FF99/YfKeVIYQFnAvuYvJKBcb76b0OItqJ3Dssq7bULcLEgIiDrb1y/1sxnTzaDVUwRM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1734578277; c=relaxed/simple; bh=1OiZGBoHLF1tR9KWeumGFiVLnSrNfbUe+083ekEYWn4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=sTSp1FjewXA3xyU0Re/G5PtVBhEzwwiv9G4vqqhaY+8z2K4xgh9OrBnv5sHBhat2WjQAYdCzFCL2gazuQ7+LwtpKxRupCWtZejiryPikIS2rWSrc52msqX9PsxBZSRWHDHrtqJvfvc8tTP039lmhhIA6HFfZBavPNHePQZ48eyw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 443873858D20 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1734578276; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to; bh=EyWYU/CVVnKxFy/9J55wP3R6+bjm8MTbqcrBj5Xonjw=; b=Diq1DghwnWkWSHn54s4BFquUV0B7QbFOk+zkhviabckq7GjXCgC5IP10pn0Wl/NvWRBd7o rAZGZVApXBSdyP4FT24Axo78vzhnYqunnA7ANpkH/8J/oT9XqrhrTSFy+BU0nAD3LNDuuK IvmWWZ6Y+gbVbSzuxnjQLCZDtFdOK0k= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-271-r3L6pGSIOmKMHrzlA__5Ig-1; Wed, 18 Dec 2024 22:17:55 -0500 X-MC-Unique: r3L6pGSIOmKMHrzlA__5Ig-1 X-Mimecast-MFC-AGG-ID: r3L6pGSIOmKMHrzlA__5Ig Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 98A83195608C for ; Thu, 19 Dec 2024 03:17:54 +0000 (UTC) Received: from greed.delorie.com (unknown [10.22.89.82]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4794130044C1; Thu, 19 Dec 2024 03:17:54 +0000 (UTC) Received: from greed.delorie.com.redhat.com (localhost [127.0.0.1]) by greed.delorie.com (8.16.1/8.16.1) with ESMTP id 4BJ3Hq3Q2221822; Wed, 18 Dec 2024 22:17:52 -0500 From: DJ Delorie To: Florian Weimer Cc: libc-alpha@sourceware.org Subject: [patch v2 1/1] assert: ensure posix compliance, add tests for such In-Reply-To: <87msi03b8s.fsf@oldenburg.str.redhat.com> (message from Florian Weimer on Fri, 15 Nov 2024 13:57:55 +0100) Date: Wed, 18 Dec 2024 22:17:52 -0500 Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 3094gJX0rE8FF5FxKW1Y8GW7H6-qUMZVqEZJpQPvrt8_1734578275 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Florian Weimer writes: > I think we should do a single write system call (or perhaps use writev), > so that the output is less likely to get interleaved. v2: Changed to create an iov and writev it out. --- Fix assert.c so that even the fallback case conforms to POSIX, although not exactly the same as the default case so a test can tell the difference. Add a test that verifies that abort is called, and that the message printed to stderr has all the info that POSIX requires. Verify this even when malloc isn't usable. diff --git a/assert/Makefile b/assert/Makefile index 35dc908ddb..6f2717c80a 100644 --- a/assert/Makefile +++ b/assert/Makefile @@ -38,6 +38,7 @@ tests := \ test-assert-perr \ tst-assert-c++ \ tst-assert-g++ \ + test-assert-2 \ # tests ifeq ($(have-cxx-thread_local),yes) diff --git a/assert/assert.c b/assert/assert.c index c29629f5f6..941b851100 100644 --- a/assert/assert.c +++ b/assert/assert.c @@ -25,6 +25,7 @@ #include #include #include +#include extern const char *__progname; @@ -87,8 +88,35 @@ __assert_fail_base (const char *fmt, const char *assertion, const char *file, else { /* At least print a minimal message. */ - static const char errstr[] = "Unexpected error.\n"; - __libc_write (STDERR_FILENO, errstr, sizeof (errstr) - 1); + char linebuf[100]; + struct iovec v[12]; + int i = 0; + + sprintf(linebuf, "%d", line); + +#define W(s) v[i].iov_base = (void *) s; v[i++].iov_len = sizeof (s) - 1; +#define WS(s) v[i].iov_base = (void *) s; v[i++].iov_len = strlen (s); + + if (__progname) + { + WS(__progname); + W(": "); + } + WS(file); + W(":"); + WS(linebuf); + W(": ") + if (function) + { + WS(function); + W(": "); + } + W("Assertion `"); + WS(assertion); + /* Intentionally different for testing purposes. */ + W("' failed\n"); + + writev (STDERR_FILENO, v, i); } abort (); diff --git a/assert/test-assert-2.c b/assert/test-assert-2.c new file mode 100644 index 0000000000..99f8f683e8 --- /dev/null +++ b/assert/test-assert-2.c @@ -0,0 +1,166 @@ +/* Test assert's compliance with POSIX requirements. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This test verifies that a failed assertion acts in accordance with + the POSIX requirements, despite any internal failures. We do so by + calling test routines via fork() and monitoring their exit codes + and stderr, while possibly forcing internal functions (malloc) to + fail. */ + +#include +#include +#include +#include +#include + +#undef NDEBUG +#include + +#include +#include +#include +#include + +/* We need to be able to make malloc() "fail" so that __asprintf + fails. */ + +void * (*next_malloc)(size_t sz) = 0; +static volatile bool fail_malloc = 0; + +void * +malloc (size_t sz) +{ + if (fail_malloc) + return NULL; + if (next_malloc == 0) + next_malloc = dlsym (RTLD_NEXT, "malloc"); + if (next_malloc == 0) + return NULL; + return next_malloc (sz); +} + +/* We can tell if abort() is called by looking for the custom return + value. */ + +void +abort_handler(int s) +{ + _exit(5); +} + +static int do_lineno; +static const char *do_funcname; + +/* Hack to get the line of the assert. */ +#define GET_INFO_1(l) \ + if (closure != NULL) \ + { \ + do_lineno = l; \ + do_funcname = __func__; \ + return; \ + } +#define GET_INFO() GET_INFO_1(__LINE__+1) + +/* These are the two test cases. */ + +static void +test_assert_normal (void *closure) +{ + if (closure == NULL) + signal (SIGABRT, abort_handler); + + GET_INFO (); + assert (1 == 2); +} + + +static void +test_assert_nomalloc (void *closure) +{ + if (closure == NULL) + { + signal (SIGABRT, abort_handler); + fail_malloc = 1; + } + + GET_INFO (); + assert (1 == 2); +} + +static void +check_posix (const char *buf, int rv, int line, + const char *funcname, const char *testarg) +{ + /* POSIX requires that a failed assertion write a diagnostic to + stderr and call abort. The diagnostic must include the filename, + line number, and function where the assertion failed, along with + the text of the assert() argument. + */ + char linestr[100]; + + sprintf (linestr, "%d", line); + + /* If abort is called, our handler will return 5. */ + TEST_VERIFY (rv == 5); + + TEST_VERIFY (strstr (buf, __FILE__) != NULL); + TEST_VERIFY (strstr (buf, linestr) != NULL); + TEST_VERIFY (strstr (buf, funcname) != NULL); + TEST_VERIFY (strstr (buf, testarg) != NULL); + +} + +static void +one_test (void (*func)(void *), int which_path) +{ + struct support_capture_subprocess sp; + int rv; + + func (&do_lineno); + printf("running test for %s, line %d\n", do_funcname, do_lineno); + + sp = support_capture_subprocess (func, NULL); + rv = WEXITSTATUS (sp.status); + + check_posix (sp.err.buffer, rv, do_lineno, do_funcname, "1 == 2"); + + /* Look for intentional subtle differences in messages to verify + that the intended code path was taken. */ + switch (which_path) + { + case 0: + TEST_VERIFY (strstr (sp.err.buffer, "failed.\n") != NULL); + break; + case 1: + TEST_VERIFY (strstr (sp.err.buffer, "failed\n") != NULL); + break; + } + + support_capture_subprocess_free (&sp); +} + +static int +do_test(void) +{ + one_test (test_assert_normal, 0); + one_test (test_assert_nomalloc, 1); + + return 0; +} + +#include