From patchwork Fri Mar 20 20:42:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 132119 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 2F42D4C9175D for ; Fri, 20 Mar 2026 20:51:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2F42D4C9175D 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=LdT/qiAX 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.133.124]) by sourceware.org (Postfix) with ESMTP id 70FB64C91741 for ; Fri, 20 Mar 2026 20:42:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 70FB64C91741 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine 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 70FB64C91741 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774039344; cv=none; b=Icp5KVFZJ3ew6uKwzcLuWqLRJwyYDlmRLtFSslDSbpSbF88QfMcXfZKvdjidijKaPVATZyg0RJK0N6wjQvTdp5Div3p2n8oQ9DAsG8RkZcrlbBu6UBk3dSzz85Ij13B29Bmimxoq+RVawDZZYYjtWKkTlL2gSRBi+uIYTiMWizs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774039344; c=relaxed/simple; bh=DNzGPsurP78OG4HhE1yVoUQjsXddS+JG8V2LH+MNzt8=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=Amf9qIYFWvl0IlDXs0np9AXJBJI3qxZWVqmg159mGmtWw4alJLDICpaTSFBqqHCpvBtlTqxY62tIQ24/ZvWWZg7MHPMmlpipXETzk4LLRsGS6nxXt7V1wuGD0DE4Pux/rC0dlvcR/AVo4wtoZppYrP+eC+mMbcVOgzfrcs+UBrI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 70FB64C91741 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774039344; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Q8uKancDflZLzKKC3OaGmnyyO/5fWQBr0uIpvIAadLY=; b=LdT/qiAX9JMJYlSOFsa03Aun6b5n+88C5JirwXbjS0qIQ4jr3yuhetMQUpqO5bHbIO3Ymc MfdKdIc8A1oF1aRAUFcHSrrnbJPicd8WkWnAwR+PlmX6nAVdAOd16Nd/ZawDbW0E0vC0PY wbyAR2LjUMtAQinW8qMSsIUCV3tuKW0= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-668-8jWi70LtOrejYq0u4OICpQ-1; Fri, 20 Mar 2026 16:42:22 -0400 X-MC-Unique: 8jWi70LtOrejYq0u4OICpQ-1 X-Mimecast-MFC-AGG-ID: 8jWi70LtOrejYq0u4OICpQ_1774039342 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 1D06C1800281 for ; Fri, 20 Mar 2026 20:42:22 +0000 (UTC) Received: from fweimer-oldenburg.csb.redhat.com (unknown [10.45.224.63]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 66DD6180075C for ; Fri, 20 Mar 2026 20:42:21 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 11/23] nscd: Add for nscd client usage In-Reply-To: Message-ID: References: X-From-Line: a216bb6a62ae3bffec9ef07887c950b324cb462a Mon Sep 17 00:00:00 2001 Date: Fri, 20 Mar 2026 21:42:18 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: eALQTA1UK83608b62T0Su_G7IHycsMweCsPAezwcv8k_1774039342 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.1 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_BLOCKED, RCVD_IN_HOSTKARMA_W, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on 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 This is very specific for parsing the shared nscd mapping. There is only enough functionality to follow the hash chain and create a consistent snapshot of the data stored at a key. Parsing the data itself should be handled through . Reviewed-by: Carlos O'Donell --- nscd/concurrent_buffer.h | 197 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 nscd/concurrent_buffer.h diff --git a/nscd/concurrent_buffer.h b/nscd/concurrent_buffer.h new file mode 100644 index 0000000000..925b9f83d1 --- /dev/null +++ b/nscd/concurrent_buffer.h @@ -0,0 +1,197 @@ +/* Parsing of concurrently modified nscd cache structures. + Copyright (C) 2026 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 + . */ + +#ifndef CONCURRENT_BUFFER_H +#define CONCURRENT_BUFFER_H + +/* Helper functions for parsing buffers that are concurrently modified. + + The helpers perform buffer bounds checking on all accesses. If an + out-of-bounds access is detected, zero or NULL is returned, and the + buffer is marked as failed. Unaligned access attempts are treated + as failure. + + Also see for a different kind of parser buffer + that does not support concurrent access. */ + +#include +#include +#include +#include + +struct concurrent_buffer +{ + const void *start; + size_t length; +}; + +/* Create a concurrent parse buffer for LENGTH bytes at START. If + LENGTH is zero, the new buffer is immediately marked as failed. */ +static inline struct concurrent_buffer +cb_create (const void *start, size_t length) +{ + return (struct concurrent_buffer) { start, length }; +} + +/* Mark *CB as failed. After that, cb_has_failed will return true. */ +static inline void +cb_mark_failed (struct concurrent_buffer *cb) +{ + cb->length = 0; +} + +/* Return true if *CB has been marked as failed. */ +static inline bool +cb_has_failed (const struct concurrent_buffer *cb) +{ + return cb->length == 0; +} + +/* Check that NEEDED bytes are available in *CB at OFFSET. Return + false on failure and fail *CB. A zero value for NEEDED is + considered failure. */ +static inline bool +cb_available (struct concurrent_buffer *cb, size_t offset, size_t needed) +{ + size_t last_byte; + if (needed == 0 + || __builtin_add_overflow (offset, needed - 1, &last_byte) + || last_byte >= cb->length) + { + cb_mark_failed (cb); + return false; + } + return true; +} + +/* Check that OFFSET leaves room for SIZE bytes in *CB, and that + OFFSET is a multiple of SIZE. */ +static inline bool +__cb_check_size_alignment (struct concurrent_buffer *cb, + size_t offset, size_t size) +{ + if (cb_available (cb, offset, size) + && (offset % size) == 0) + return true; + else + { + cb_mark_failed (cb); + return false; + } +} + +/* Extract an unsigned 8-bit value at OFFSET. If *CB contains only + OFFSET or fewer bytes, fail *CB and return 0. */ +static inline uint8_t +cb_u8 (struct concurrent_buffer *cb, size_t offset) +{ + if (offset >= cb->length) + { + cb_mark_failed (cb); + return 0; + } + /* Relaxed MO is sufficient. We cannot use atomics because one-byte + inline atomics are not supported on all glibc targets. */ + return *((volatile uint8_t *) cb->start + offset); +} + +/* Extract an unsigned 32-bit value at OFFSET. If 4 bytes are not + available, fail *CB and return 0. */ +static inline uint32_t +cb_u32 (struct concurrent_buffer *cb, size_t offset) +{ + if (__cb_check_size_alignment (cb, offset, 4)) + return atomic_load_relaxed ((uint32_t *) ((char *) cb->start + offset)); + else + return 0; +} + +/* Extract an unsigned 64-bit value at OFFSET. If 8 bytes are not + available, fail *CB and return 0. Always fail if 64-bit atomics + are unavailable. */ +static inline uint64_t +cb_u64 (struct concurrent_buffer *cb, size_t offset) +{ +#if HAVE_64B_ATOMICS + if (__cb_check_size_alignment (cb, offset, 8)) + return atomic_load_relaxed ((uint64_t *) ((char *) cb->start + offset)); + else + return 0; +#else + cb_mark_failed (cb); + return 0; +#endif +} + +/* Extract a signed 32-bit value at OFFSET. If 4 bytes are not + available, fail *CB and return 0. */ +static inline int32_t +cb_s32 (struct concurrent_buffer *cb, size_t offset) +{ + /* Rely on GCC extension for converting to signed. */ + return cb_u32 (cb, offset); +} + +/* Extract a signed 64-bit value at OFFSET. If 8 bytes are not + available, fail *CB and return 0. Always fail if 64-bit atomics + are unavailable. */ +static inline int64_t +cb_s64 (struct concurrent_buffer *cb, size_t offset) +{ + /* Rely on GCC extension for converting to signed. */ + return cb_u64 (cb, offset); +} + +/* Return A + B. On overflow, return 0 and mark *CB as failed. */ +static inline size_t +cb_add (struct concurrent_buffer *cb, size_t a, size_t b) +{ + size_t result; + if (__builtin_add_overflow (a, b, &result)) + { + cb_mark_failed (cb); + return 0; + } + return result; +} + +/* Extract field MEMBER of the struct type STYP from *CB and return + its value. The struct starts at OFFSET. Fail *CB and return zero + if the struct field is not available in *CB (the full struct does + not need to be available). */ +#define cb_field(cb, offset, styp, member) \ + (_Generic ((styp) { }.member, \ + uint8_t: cb_u8, \ + uint32_t: cb_u32, \ + int32_t: cb_s32, \ + int64_t: cb_s64) \ + (cb, cb_add (cb, offset, offsetof (styp, member)))) + +/* Return true if the LENGTH bytes at OFFSET match TO_COMPARE, false + otherwise. If LENGTH bytes are not available, mark *CB as failed. + If the comparison fails, *CB is not marked as failed. */ +static inline bool +cb_memeq (struct concurrent_buffer *cb, size_t offset, + const void *to_compare, size_t length) +{ + if (! cb_available (cb, offset, length)) + return false; + return memcmp ((char *) cb->start + offset, to_compare, length) == 0; +} + +#endif /* CONCURRENT_BUFFER_H */