From patchwork Fri Jun 5 10:29:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rasmus Villemoes X-Patchwork-Id: 1304052 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=prevas.dk Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=prevas.dk header.i=@prevas.dk header.a=rsa-sha256 header.s=selector1 header.b=ZpmFTQzZ; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49df5y6Gybz9sSF for ; Fri, 5 Jun 2020 20:29:42 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 2B6F981E1E; Fri, 5 Jun 2020 12:29:37 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=prevas.dk Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=prevas.dk header.i=@prevas.dk header.b="ZpmFTQzZ"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 3A80781E35; Fri, 5 Jun 2020 12:29:35 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FORGED_SPF_HELO,MSGID_FROM_MTA_HEADER, SPF_HELO_PASS,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 Received: from EUR05-VI1-obe.outbound.protection.outlook.com (mail-vi1eur05on20729.outbound.protection.outlook.com [IPv6:2a01:111:f400:7d00::729]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 2406281E00 for ; Fri, 5 Jun 2020 12:29:31 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=prevas.dk Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=rasmus.villemoes@prevas.dk ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=a+WECm7850j+lxgPwOqYnDWoHop0/OiklqTDjzL7Nt+Ph1eSj4gIVNYSi+v/C3sMLpzWXqfq3hLDZFn3Ns2LXcw/JUj6HRM9gWuYst+BYmBI2tW/xShyEkZqL02fZB7JqtM2BNEUXgQX4NDN5YFQgSBm1v9pioZS9v3FmKKo0kO6ps8ytzffi4SM2z2pPuB9Yvr09TqJ3g0slRzgH62IyQ/QZn26Hu+Le3k4xTd9H3GVo/OvNu+YDK2JW8cPQEPL3aSny8IkzcUpHDjj7ZqdyjNHfKZdRwQXMaSTo34u9zR9f6cbYOFPaofMChd4w4X57L6cSVsi2Abu4uSBnkagBg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=2NhqiRweRRDdctfwkfAKsPiXYZ7W4MenUu4RLiTs8Qs=; b=AfzSmCNFwHeLqMoudMSDqCac/uTDNIihu0ZZ28pWLve5GvspvNKBQ9jyujOxyZWBtYX5mmqsksH8ZGKDamlhPVNR3+XA2+/3ILe+MT0qe7xWWIKR0msnl92khUFJibzUhfDQIJlaUYd/rFK7Osz05TI6T5RD/gUQ+TP/pKWP9YK4P+K8B282xy8hIv0vms8S2p12nXrXd7LXrlHI50E6Frq8QMyj5wVMOT2x59+D8Wp0bJxDx2eK/uO6D4nKRC0HToCgDBMWj3jXwPkrh9lDQ3teAu4zDNY8DeOzGq4LJm7PImpRfDgRpmHnmXVOjkHjCPN1JQs6dogRV3kubQ+e6g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=prevas.dk; dmarc=pass action=none header.from=prevas.dk; dkim=pass header.d=prevas.dk; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=prevas.dk; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=2NhqiRweRRDdctfwkfAKsPiXYZ7W4MenUu4RLiTs8Qs=; b=ZpmFTQzZBw43jFuDxeckc+UqErCsilFXRZyHZuWsAPFwqyJZyGpnaNeKMTbQTXdloQSAg0dfgNZ2rTHrKpEemRiO4ydAvtx35dbYwWTFM61LXNw1hCh7M3NKmjX1Tte085HiCIG+MJMxowOJEHoOIamX3fJP+vBz2Vc7Sw9r2KE= Authentication-Results: lists.denx.de; dkim=none (message not signed) header.d=none;lists.denx.de; dmarc=none action=none header.from=prevas.dk; Received: from VI1PR10MB2765.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:803:e1::21) by VI1PR10MB1584.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:802:35::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3066.20; Fri, 5 Jun 2020 10:29:28 +0000 Received: from VI1PR10MB2765.EURPRD10.PROD.OUTLOOK.COM ([fe80::f0ac:4e97:2536:faa]) by VI1PR10MB2765.EURPRD10.PROD.OUTLOOK.COM ([fe80::f0ac:4e97:2536:faa%7]) with mapi id 15.20.3066.018; Fri, 5 Jun 2020 10:29:28 +0000 From: Rasmus Villemoes To: u-boot@lists.denx.de Cc: Julius Werner , Simon Glass , Tom Rini , Rasmus Villemoes Subject: [PATCH] lz4: fix decompressor on big-endian powerpc Date: Fri, 5 Jun 2020 12:29:20 +0200 Message-Id: <20200605102920.1545-1-rasmus.villemoes@prevas.dk> X-Mailer: git-send-email 2.23.0 X-ClientProxiedBy: AM6P195CA0099.EURP195.PROD.OUTLOOK.COM (2603:10a6:209:86::40) To VI1PR10MB2765.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:803:e1::21) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from prevas-ravi.prevas.se (5.186.116.45) by AM6P195CA0099.EURP195.PROD.OUTLOOK.COM (2603:10a6:209:86::40) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3066.18 via Frontend Transport; Fri, 5 Jun 2020 10:29:27 +0000 X-Mailer: git-send-email 2.23.0 X-Originating-IP: [5.186.116.45] X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 0b580c6b-df60-4073-5a76-08d8093b5372 X-MS-TrafficTypeDiagnostic: VI1PR10MB1584: X-MS-Exchange-Transport-Forked: True X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:10000; X-Forefront-PRVS: 0425A67DEF X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: PsmSsmUIy9uFHDu0OTyvRcx9sc2Tm96n80UNdjwszYxS1aPh42JduIlgrhWMVA68qUe8/2ztoPJlt/OhJ2y3rJilIeDDiMtg0adK0O8QLuVssXsEl9vVkZ3C8fhKswAIImWudNc04L7GsurBGMdmivZC/TciAZLVEINEWQowl40diM1yOYV8hsIHwfieQUxGBrEpZ28fb0XnaUmpojb+iyvUOwjGKEkshgi4WpKboFHqdlpcqDXcdrwwNp+hlHFlZjzrVvIFlOXqFzVfeyJnSwkMvTlFeIdjnrorAt8MGdA7Bxikj3BPQJ8icsW4674dQxXXLD3hpZFg1CIlqDgRtw== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:VI1PR10MB2765.EURPRD10.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFTY:; SFS:(396003)(376002)(346002)(136003)(39850400004)(366004)(16526019)(6916009)(186003)(86362001)(6506007)(8676002)(478600001)(54906003)(8976002)(83380400001)(107886003)(66556008)(66476007)(4326008)(66946007)(6666004)(6486002)(8936002)(26005)(1076003)(5660300002)(6512007)(2616005)(44832011)(36756003)(2906002)(52116002)(956004)(316002); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData: Mp5n5nkiSBgFhg8EYzoB65EKN7ySDR7jddwnrN2IBYbGg4pAcYGpUZzp+vZIs7wWxfhOi0CBhZTPIAvOH2FjyhWVj9xjMhW01Dkg8/axBsPUSPlqOqU/ahw5lJF04uZMN5RMhcsAr2B/Ze+FwGjoyVLK7B1dPrf+ca0HIf7+RsPZXSu4FaXAb2bE4PHLJAd3mDxFuCleoxtf0HLFqEiJvvANu8hB8z0HGzyC6Mi0zmanmZAopUfIXx/oBtXv4NCDZ2qtRaL1yLUawXiPS7Fj2NnvFJPOMRW0IVO15hrigGVeh+4Tl1wmrtONdQN98fv1pbHnvakThcA4Iy/PKPcz5JMlcYL/5ZInBS+BXj+YJGkvL2LfFCu4DgqRDrggPTqoYIGjW09D9IHAqgjqQcITJSF9d5hIk1prfSoDlxfTnbNtcsAI5+dJ/jsPWPSSZV6eQDVCCs8F9tsnj23n1C6UUNvWg9+Xwj0Nhb87DeB7uTU= X-OriginatorOrg: prevas.dk X-MS-Exchange-CrossTenant-Network-Message-Id: 0b580c6b-df60-4073-5a76-08d8093b5372 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 Jun 2020 10:29:28.2038 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: d350cf71-778d-4780-88f5-071a4cb1ed61 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: P6SG/c+iMBUzG+QDcwILa1FbIaRreT7+d9ESDgq4bR9aqtsPLIUhCbivHfybmq/N/j7I29iI1315O2cEyWMGYiVADchQ7ZImJPZIRJV/+lI= X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR10MB1584 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean Booting an lz4-compressed kernel image fails on our powerpc board with -EPROTONOSUPPORT. Adding a bit of debug prints, we get magic: 0x184d2204 flags: 0x64 reserved0: 1 has_content_checksum: 1 has_content_size: 0 has_block_checksum: 0 independent_blocks: 1 version: 0 block_descriptor: 70 reserved1: 7 max_block_size: 0 reserved2: 0 So the magic is ok, but the version check fails, also some reserved bits are apparently set. But that's because the code interprets the "flags" and "block_descriptor" bytes wrongly: Using bit-fields to access individual bits of an "on the wire" format is not portable, not even when restricted to the C flavour implemented by gcc. Quoting the gcc manual: * 'The order of allocation of bit-fields within a unit (C90 6.5.2.1, C99 and C11 6.7.2.1).' Determined by ABI. and indeed, the PPC Processor ABI supplement says * Bit-fields are allocated from right to left (least to most significant) on Little-Endian implementations and from left to right (most to least significant) on Big-Endian implementations. I'm a bit puzzled about the github.com/Cyan4973/lz4 reference. Cyan4973 is Yann Collet, the author of lz4, and nowadays at least that github url redirects to the upstream repo, github.com/lz4/lz4. Grepping through all available tags and branches there, the identifiers "has_content_checksum" and "lz4_frame_header" have never appeared anywhere in the source (and he uses camelCase). In any case, the upstream code at least now uses explicit shifts and masks for encoding/decoding: /* FLG Byte */ *dstPtr++ = (BYTE)(((1 & _2BITS) << 6) /* Version('01') */ + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5) + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4) + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3) + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2) + (cctxPtr->prefs.frameInfo.dictID > 0) ); /* Flags */ { U32 const FLG = srcPtr[4]; U32 const version = (FLG>>6) & _2BITS; blockChecksumFlag = (FLG>>4) & _1BIT; blockMode = (FLG>>5) & _1BIT; contentSizeFlag = (FLG>>3) & _1BIT; contentChecksumFlag = (FLG>>2) & _1BIT; dictIDFlag = FLG & _1BIT; /* validate */ if (((FLG>>1)&_1BIT) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */ if (version != 1) return err0r(LZ4F_ERROR_headerVersion_wrong); /* Version Number, only supported value */ } Do the same here, and while at it, be more careful to use unaligned accessors to what is most likely unaligned. This has been tested partly, of course, by seeing that my lz4-compressed kernel now boots, partly by running the (de)compression test-suite in the (x86_64) sandbox - i.e., it should still work just fine on little-endian hosts. Signed-off-by: Rasmus Villemoes Reviewed-by: Julius Werner --- lib/lz4_wrapper.c | 96 ++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/lib/lz4_wrapper.c b/lib/lz4_wrapper.c index 1e1e8d5085..16ffff5acb 100644 --- a/lib/lz4_wrapper.c +++ b/lib/lz4_wrapper.c @@ -9,6 +9,7 @@ #include #include #include +#include static u16 LZ4_readLE16(const void *src) { return le16_to_cpu(*(u16 *)src); } static void LZ4_copy4(void *dst, const void *src) { *(u32 *)dst = *(u32 *)src; } @@ -22,45 +23,11 @@ typedef uint64_t U64; #define FORCE_INLINE static inline __attribute__((always_inline)) -/* Unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */ +/* Originally from github.com/Cyan4973/lz4, fixed to avoid relying on bit-field ordering. */ + #include "lz4.c" /* #include for inlining, do not link! */ -struct lz4_frame_header { - u32 magic; - union { - u8 flags; - struct { - u8 reserved0:2; - u8 has_content_checksum:1; - u8 has_content_size:1; - u8 has_block_checksum:1; - u8 independent_blocks:1; - u8 version:2; - }; - }; - union { - u8 block_descriptor; - struct { - u8 reserved1:4; - u8 max_block_size:3; - u8 reserved2:1; - }; - }; - /* + u64 content_size iff has_content_size is set */ - /* + u8 header_checksum */ -} __packed; - -struct lz4_block_header { - union { - u32 raw; - struct { - u32 size:31; - u32 not_compressed:1; - }; - }; - /* + size bytes of data */ - /* + u32 block_checksum iff has_block_checksum is set */ -} __packed; +#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn) { @@ -72,53 +39,70 @@ int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn) *dstn = 0; { /* With in-place decompression the header may become invalid later. */ - const struct lz4_frame_header *h = in; + u32 magic; + u8 flags, version, independent_blocks, has_content_size; + u8 block_desc; - if (srcn < sizeof(*h) + sizeof(u64) + sizeof(u8)) + if (srcn < sizeof(u32) + 3*sizeof(u8)) return -EINVAL; /* input overrun */ + magic = get_unaligned_le32(in); + in += sizeof(u32); + flags = *(u8 *)in; + in += sizeof(u8); + block_desc = *(u8 *)in; + in += sizeof(u8); + + version = (flags >> 6) & 0x3; + independent_blocks = (flags >> 5) & 0x1; + has_block_checksum = (flags >> 4) & 0x1; + has_content_size = (flags >> 3) & 0x1; + /* We assume there's always only a single, standard frame. */ - if (le32_to_cpu(h->magic) != LZ4F_MAGIC || h->version != 1) + if (magic != LZ4F_MAGIC || version != 1) return -EPROTONOSUPPORT; /* unknown format */ - if (h->reserved0 || h->reserved1 || h->reserved2) - return -EINVAL; /* reserved must be zero */ - if (!h->independent_blocks) + if ((flags & 0x03) || (block_desc & 0x8f)) + return -EINVAL; /* reserved bits must be zero */ + if (!independent_blocks) return -EPROTONOSUPPORT; /* we can't support this yet */ - has_block_checksum = h->has_block_checksum; - in += sizeof(*h); - if (h->has_content_size) + if (has_content_size) { + if (srcn < sizeof(u32) + 3*sizeof(u8) + sizeof(u64)) + return -EINVAL; /* input overrun */ in += sizeof(u64); + } + /* Header checksum byte */ in += sizeof(u8); } while (1) { - struct lz4_block_header b; + u32 block_header, block_size; - b.raw = le32_to_cpu(*(u32 *)in); - in += sizeof(struct lz4_block_header); + block_header = get_unaligned_le32(in); + in += sizeof(u32); + block_size = block_header & ~LZ4F_BLOCKUNCOMPRESSED_FLAG; - if (in - src + b.size > srcn) { + if (in - src + block_size > srcn) { ret = -EINVAL; /* input overrun */ break; } - if (!b.size) { + if (!block_size) { ret = 0; /* decompression successful */ break; } - if (b.not_compressed) { - size_t size = min((ptrdiff_t)b.size, end - out); + if (block_header & LZ4F_BLOCKUNCOMPRESSED_FLAG) { + size_t size = min((ptrdiff_t)block_size, end - out); memcpy(out, in, size); out += size; - if (size < b.size) { + if (size < block_size) { ret = -ENOBUFS; /* output overrun */ break; } } else { /* constant folding essential, do not touch params! */ - ret = LZ4_decompress_generic(in, out, b.size, + ret = LZ4_decompress_generic(in, out, block_size, end - out, endOnInputSize, full, 0, noDict, out, NULL, 0); if (ret < 0) { @@ -128,7 +112,7 @@ int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn) out += ret; } - in += b.size; + in += block_size; if (has_block_checksum) in += sizeof(u32); }