Misc VNC, I/O, Crypto & checkpatch changes

* Fix VNC tight encoding with 8/16-bpp formats with
   mixed endian server/client
 * Fix VNC non-tight encoding with mixed endian server/client
 * Drop built-in AES impl from non-TCG usage, requiring
   nettle/gcrypt/gnutls
 * Fix validation of SPDX-License-Identifier in new files
 * Mandate SPDX-License-Identifier in Rust source
 * Reject license boilerplate in new files
 * Add full control over TCP keep alive setting for sockets
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmgu/EgACgkQvobrtBUQ
 T994FA/7BLeIHJqsV3/DtPKVqllzG2PJT/n85Owu/h39gqRsqHDssDQFPmgFsnzk
 UcvOSLd7RKQ5/tY5zLPh4JPpnloJ/jpj50hUK42wu8Q4U16PV/yUhQVVjEkVmX+z
 XepbEwgrEVuy4F62NnUJmbaT5PcayyS5FPREbrQ8zPzagMWTSqbR7EQ+PCTUkJdo
 LR4mvxoqWhGnaQzPAGlRtAfRfT6Jg3NaL4sLqLiexuhdloZLHC85SvE1usBg8x+M
 KP2BX1FeIILnN+1CXnZ9/vzUqiFaFLfzGwVMK9QYW0GW2Oo3uCcLloY+llbo6Pq/
 tC8Po8AMIOojnrJm+TeS6V18QBNU5qqyHKGamZrSlBobZRgC7tOSljExoT5mnGrS
 V1nKNAz5FLz7LQ8jZpziPlPqr3WBqBRtV8SxJD+a0vh0/5YnTCbPC0Q6Q2N8cQDh
 Wra9QN10xD60tjsnRT/7Lp7gW/RyjT+uJHQkNxn6PZVbI/6Q1283YpbmVY55vcNe
 De47LPsmc6XnpJSmzmjt+VrWLob67IOo4JcttMrv7xWj08jb1TFUf7M0Mvdu2YBR
 3C9MAt5sjmL9qHARToXr8RC3SCX9pMTZFYatHGAbRdRDi6ygFW1OQVJvxrOj00kN
 bavXjcDlTfRzgTnVRbqUbqSY0D9LZqSUDRxfQdEBGAzWgMksAuM=
 =X323
 -----END PGP SIGNATURE-----

Merge tag 'misc-next-pull-request' of https://gitlab.com/berrange/qemu into staging

Misc VNC, I/O, Crypto & checkpatch changes

* Fix VNC tight encoding with 8/16-bpp formats with
  mixed endian server/client
* Fix VNC non-tight encoding with mixed endian server/client
* Drop built-in AES impl from non-TCG usage, requiring
  nettle/gcrypt/gnutls
* Fix validation of SPDX-License-Identifier in new files
* Mandate SPDX-License-Identifier in Rust source
* Reject license boilerplate in new files
* Add full control over TCP keep alive setting for sockets

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmgu/EgACgkQvobrtBUQ
# T994FA/7BLeIHJqsV3/DtPKVqllzG2PJT/n85Owu/h39gqRsqHDssDQFPmgFsnzk
# UcvOSLd7RKQ5/tY5zLPh4JPpnloJ/jpj50hUK42wu8Q4U16PV/yUhQVVjEkVmX+z
# XepbEwgrEVuy4F62NnUJmbaT5PcayyS5FPREbrQ8zPzagMWTSqbR7EQ+PCTUkJdo
# LR4mvxoqWhGnaQzPAGlRtAfRfT6Jg3NaL4sLqLiexuhdloZLHC85SvE1usBg8x+M
# KP2BX1FeIILnN+1CXnZ9/vzUqiFaFLfzGwVMK9QYW0GW2Oo3uCcLloY+llbo6Pq/
# tC8Po8AMIOojnrJm+TeS6V18QBNU5qqyHKGamZrSlBobZRgC7tOSljExoT5mnGrS
# V1nKNAz5FLz7LQ8jZpziPlPqr3WBqBRtV8SxJD+a0vh0/5YnTCbPC0Q6Q2N8cQDh
# Wra9QN10xD60tjsnRT/7Lp7gW/RyjT+uJHQkNxn6PZVbI/6Q1283YpbmVY55vcNe
# De47LPsmc6XnpJSmzmjt+VrWLob67IOo4JcttMrv7xWj08jb1TFUf7M0Mvdu2YBR
# 3C9MAt5sjmL9qHARToXr8RC3SCX9pMTZFYatHGAbRdRDi6ygFW1OQVJvxrOj00kN
# bavXjcDlTfRzgTnVRbqUbqSY0D9LZqSUDRxfQdEBGAzWgMksAuM=
# =X323
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 22 May 2025 06:28:24 EDT
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [full]
# Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E  8E3F BE86 EBB4 1510 4FDF

* tag 'misc-next-pull-request' of https://gitlab.com/berrange/qemu: (23 commits)
  scripts/checkpatch.pl: mandate SPDX tag for Rust src files
  util/qemu-sockets: Introduce inet socket options controlling TCP keep-alive
  util/qemu-sockets: Refactor inet_parse() to use QemuOpts
  util/qemu-sockets: Add support for keep-alive flag to passive sockets
  util/qemu-sockets: Refactor success and failure paths in inet_listen_saddr()
  util/qemu-sockets: Refactor setting client sockopts into a separate function
  io: Fix partial struct copy in qio_dns_resolver_lookup_sync_inet()
  scripts/checkpatch: reject license boilerplate on new files
  scripts/checkpatch: reimplement mandate for SPDX-License-Identifier
  scripts/checkpatch: use new hook for MAINTAINERS update check
  scripts/checkpatch: expand pattern for matching makefiles
  scripts/checkpatch: use new hook for file permissions check
  scripts/checkpatch: use new hook for ACPI test data check
  scripts/checkpatch: introduce tracking of file start/end
  scripts/checkpatch.pl: fix various indentation mistakes
  Revert "scripts: mandate that new files have SPDX-License-Identifier"
  crypto: fully drop built-in cipher provider
  tests: fix skipping cipher tests when AES is not available
  tests: skip legacy qcow2 encryption test if AES is not available
  tests: skip encrypted secret tests if AES is not available
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-05-22 14:48:55 -04:00
commit 4589acc17a
19 changed files with 851 additions and 630 deletions

View file

@ -1,303 +0,0 @@
/*
* QEMU Crypto cipher built-in algorithms
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This 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.
*
* This 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 this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "crypto/aes.h"
typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext;
struct QCryptoCipherBuiltinAESContext {
AES_KEY enc;
AES_KEY dec;
};
typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
struct QCryptoCipherBuiltinAES {
QCryptoCipher base;
QCryptoCipherBuiltinAESContext key;
uint8_t iv[AES_BLOCK_SIZE];
};
static inline bool qcrypto_length_check(size_t len, size_t blocksize,
Error **errp)
{
if (unlikely(len & (blocksize - 1))) {
error_setg(errp, "Length %zu must be a multiple of block size %zu",
len, blocksize);
return false;
}
return true;
}
static void qcrypto_cipher_ctx_free(QCryptoCipher *cipher)
{
g_free(cipher);
}
static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher,
const uint8_t *iv, size_t niv,
Error **errp)
{
error_setg(errp, "Setting IV is not supported");
return -1;
}
static void do_aes_encrypt_ecb(const void *vctx,
size_t len,
uint8_t *out,
const uint8_t *in)
{
const QCryptoCipherBuiltinAESContext *ctx = vctx;
/* We have already verified that len % AES_BLOCK_SIZE == 0. */
while (len) {
AES_encrypt(in, out, &ctx->enc);
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
}
}
static void do_aes_decrypt_ecb(const void *vctx,
size_t len,
uint8_t *out,
const uint8_t *in)
{
const QCryptoCipherBuiltinAESContext *ctx = vctx;
/* We have already verified that len % AES_BLOCK_SIZE == 0. */
while (len) {
AES_decrypt(in, out, &ctx->dec);
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
}
}
static void do_aes_encrypt_cbc(const AES_KEY *key,
size_t len,
uint8_t *out,
const uint8_t *in,
uint8_t *ivec)
{
uint8_t tmp[AES_BLOCK_SIZE];
size_t n;
/* We have already verified that len % AES_BLOCK_SIZE == 0. */
while (len) {
for (n = 0; n < AES_BLOCK_SIZE; ++n) {
tmp[n] = in[n] ^ ivec[n];
}
AES_encrypt(tmp, out, key);
memcpy(ivec, out, AES_BLOCK_SIZE);
len -= AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
}
static void do_aes_decrypt_cbc(const AES_KEY *key,
size_t len,
uint8_t *out,
const uint8_t *in,
uint8_t *ivec)
{
uint8_t tmp[AES_BLOCK_SIZE];
size_t n;
/* We have already verified that len % AES_BLOCK_SIZE == 0. */
while (len) {
memcpy(tmp, in, AES_BLOCK_SIZE);
AES_decrypt(in, out, key);
for (n = 0; n < AES_BLOCK_SIZE; ++n) {
out[n] ^= ivec[n];
}
memcpy(ivec, tmp, AES_BLOCK_SIZE);
len -= AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
}
static int qcrypto_cipher_aes_encrypt_ecb(QCryptoCipher *cipher,
const void *in, void *out,
size_t len, Error **errp)
{
QCryptoCipherBuiltinAES *ctx
= container_of(cipher, QCryptoCipherBuiltinAES, base);
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
return -1;
}
do_aes_encrypt_ecb(&ctx->key, len, out, in);
return 0;
}
static int qcrypto_cipher_aes_decrypt_ecb(QCryptoCipher *cipher,
const void *in, void *out,
size_t len, Error **errp)
{
QCryptoCipherBuiltinAES *ctx
= container_of(cipher, QCryptoCipherBuiltinAES, base);
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
return -1;
}
do_aes_decrypt_ecb(&ctx->key, len, out, in);
return 0;
}
static int qcrypto_cipher_aes_encrypt_cbc(QCryptoCipher *cipher,
const void *in, void *out,
size_t len, Error **errp)
{
QCryptoCipherBuiltinAES *ctx
= container_of(cipher, QCryptoCipherBuiltinAES, base);
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
return -1;
}
do_aes_encrypt_cbc(&ctx->key.enc, len, out, in, ctx->iv);
return 0;
}
static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher,
const void *in, void *out,
size_t len, Error **errp)
{
QCryptoCipherBuiltinAES *ctx
= container_of(cipher, QCryptoCipherBuiltinAES, base);
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
return -1;
}
do_aes_decrypt_cbc(&ctx->key.dec, len, out, in, ctx->iv);
return 0;
}
static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv,
size_t niv, Error **errp)
{
QCryptoCipherBuiltinAES *ctx
= container_of(cipher, QCryptoCipherBuiltinAES, base);
if (niv != AES_BLOCK_SIZE) {
error_setg(errp, "IV must be %d bytes not %zu",
AES_BLOCK_SIZE, niv);
return -1;
}
memcpy(ctx->iv, iv, AES_BLOCK_SIZE);
return 0;
}
static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_ecb = {
.cipher_encrypt = qcrypto_cipher_aes_encrypt_ecb,
.cipher_decrypt = qcrypto_cipher_aes_decrypt_ecb,
.cipher_setiv = qcrypto_cipher_no_setiv,
.cipher_free = qcrypto_cipher_ctx_free,
};
static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = {
.cipher_encrypt = qcrypto_cipher_aes_encrypt_cbc,
.cipher_decrypt = qcrypto_cipher_aes_decrypt_cbc,
.cipher_setiv = qcrypto_cipher_aes_setiv,
.cipher_free = qcrypto_cipher_ctx_free,
};
bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
QCryptoCipherMode mode)
{
switch (alg) {
case QCRYPTO_CIPHER_ALGO_AES_128:
case QCRYPTO_CIPHER_ALGO_AES_192:
case QCRYPTO_CIPHER_ALGO_AES_256:
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_CBC:
return true;
default:
return false;
}
break;
default:
return false;
}
}
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
QCryptoCipherMode mode,
const uint8_t *key,
size_t nkey,
Error **errp)
{
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL;
}
switch (alg) {
case QCRYPTO_CIPHER_ALGO_AES_128:
case QCRYPTO_CIPHER_ALGO_AES_192:
case QCRYPTO_CIPHER_ALGO_AES_256:
{
QCryptoCipherBuiltinAES *ctx;
const QCryptoCipherDriver *drv;
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
drv = &qcrypto_cipher_aes_driver_ecb;
break;
case QCRYPTO_CIPHER_MODE_CBC:
drv = &qcrypto_cipher_aes_driver_cbc;
break;
default:
goto bad_mode;
}
ctx = g_new0(QCryptoCipherBuiltinAES, 1);
ctx->base.driver = drv;
if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) {
error_setg(errp, "Failed to set encryption key");
goto error;
}
if (AES_set_decrypt_key(key, nkey * 8, &ctx->key.dec)) {
error_setg(errp, "Failed to set decryption key");
goto error;
}
return &ctx->base;
error:
g_free(ctx);
return NULL;
}
default:
error_setg(errp,
"Unsupported cipher algorithm %s",
QCryptoCipherAlgo_str(alg));
return NULL;
}
bad_mode:
error_setg(errp, "Unsupported cipher mode %s",
QCryptoCipherMode_str(mode));
return NULL;
}

30
crypto/cipher-stub.c.inc Normal file
View file

@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* QEMU Crypto cipher impl stub
*
* Copyright (c) 2025 Red Hat, Inc.
*
*/
bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
QCryptoCipherMode mode)
{
return false;
}
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
QCryptoCipherMode mode,
const uint8_t *key,
size_t nkey,
Error **errp)
{
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL;
}
error_setg(errp,
"Unsupported cipher algorithm %s, no crypto library enabled in build",
QCryptoCipherAlgo_str(alg));
return NULL;
}

View file

@ -145,7 +145,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg,
#elif defined CONFIG_GNUTLS_CRYPTO
#include "cipher-gnutls.c.inc"
#else
#include "cipher-builtin.c.inc"
#include "cipher-stub.c.inc"
#endif
QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgo alg,

View file

@ -75,12 +75,12 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format);
pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman);
int qemu_pixman_get_type(int rshift, int gshift, int bshift);
int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian);
bool qemu_pixman_check_format(DisplayChangeListener *dcl,
pixman_format_code_t format);
#ifdef CONFIG_PIXMAN
pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf);
pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian);
pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
int width);
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,

View file

@ -111,22 +111,11 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver,
uaddr, INET6_ADDRSTRLEN, uport, 32,
NI_NUMERICHOST | NI_NUMERICSERV);
newaddr->u.inet = (InetSocketAddress){
.host = g_strdup(uaddr),
.port = g_strdup(uport),
.has_numeric = true,
.numeric = true,
.has_to = iaddr->has_to,
.to = iaddr->to,
.has_ipv4 = iaddr->has_ipv4,
.ipv4 = iaddr->ipv4,
.has_ipv6 = iaddr->has_ipv6,
.ipv6 = iaddr->ipv6,
#ifdef HAVE_IPPROTO_MPTCP
.has_mptcp = iaddr->has_mptcp,
.mptcp = iaddr->mptcp,
#endif
};
newaddr->u.inet = *iaddr;
newaddr->u.inet.host = g_strdup(uaddr),
newaddr->u.inet.port = g_strdup(uport),
newaddr->u.inet.has_numeric = true,
newaddr->u.inet.numeric = true,
(*addrs)[i] = newaddr;
}

View file

@ -2760,6 +2760,36 @@ if linux_io_uring.found()
config_host_data.set('HAVE_IO_URING_PREP_WRITEV2',
cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2'))
endif
config_host_data.set('HAVE_TCP_KEEPCNT',
cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPCNT') or
cc.compiles('''
#include <ws2tcpip.h>
#ifndef TCP_KEEPCNT
#error
#endif
int main(void) { return 0; }''',
name: 'Win32 TCP_KEEPCNT'))
# On Darwin TCP_KEEPIDLE is available under different name, TCP_KEEPALIVE.
# https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/man/man4/tcp.4#L172
config_host_data.set('HAVE_TCP_KEEPIDLE',
cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE') or
cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPALIVE') or
cc.compiles('''
#include <ws2tcpip.h>
#ifndef TCP_KEEPIDLE
#error
#endif
int main(void) { return 0; }''',
name: 'Win32 TCP_KEEPIDLE'))
config_host_data.set('HAVE_TCP_KEEPINTVL',
cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPINTVL') or
cc.compiles('''
#include <ws2tcpip.h>
#ifndef TCP_KEEPINTVL
#error
#endif
int main(void) { return 0; }''',
name: 'Win32 TCP_KEEPINTVL'))
# has_member
config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID',

View file

@ -56,8 +56,24 @@
# @ipv6: whether to accept IPv6 addresses, default try both IPv4 and
# IPv6
#
# @keep-alive: enable keep-alive when connecting to this socket. Not
# supported for passive sockets. (Since 4.2)
# @keep-alive: enable keep-alive when connecting to/listening on this socket.
# (Since 4.2, not supported for listening sockets until 10.1)
#
# @keep-alive-count: number of keep-alive packets sent before the connection is
# closed. Only supported for TCP sockets on systems where TCP_KEEPCNT
# socket option is defined (this includes Linux, Windows, macOS, FreeBSD,
# but not OpenBSD). When set to 0, system setting is used. (Since 10.1)
#
# @keep-alive-idle: time in seconds the connection needs to be idle before
# sending a keepalive packet. Only supported for TCP sockets on systems
# where TCP_KEEPIDLE socket option is defined (this includes Linux,
# Windows, macOS, FreeBSD, but not OpenBSD). When set to 0, system setting
# is used. (Since 10.1)
#
# @keep-alive-interval: time in seconds between keep-alive packets. Only
# supported for TCP sockets on systems where TCP_KEEPINTVL is defined (this
# includes Linux, Windows, macOS, FreeBSD, but not OpenBSD). When set to
# 0, system setting is used. (Since 10.1)
#
# @mptcp: enable multi-path TCP. (Since 6.1)
#
@ -71,6 +87,9 @@
'*ipv4': 'bool',
'*ipv6': 'bool',
'*keep-alive': 'bool',
'*keep-alive-count': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPCNT' },
'*keep-alive-idle': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPIDLE' },
'*keep-alive-interval': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPINTVL' },
'*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } }
##

View file

@ -365,6 +365,18 @@ our @typeList = (
qr{guintptr},
);
# Match text found in common license boilerplate comments:
# for new files the SPDX-License-Identifier line is sufficient.
our @LICENSE_BOILERPLATE = (
"licensed under the terms of the GNU GPL",
"under the terms of the GNU General Public License",
"under the terms of the GNU Lesser General Public",
"Permission is hereby granted, free of charge",
"GNU GPL, version 2 or later",
"See the COPYING file"
);
our $LICENSE_BOILERPLATE_RE = join("|", @LICENSE_BOILERPLATE);
# Load common spelling mistakes and build regular expression list.
my $misspellings;
my %spelling_fix;
@ -1330,29 +1342,6 @@ sub WARN {
}
}
# According to tests/qtest/bios-tables-test.c: do not
# change expected file in the same commit with adding test
sub checkfilename {
my ($name, $acpi_testexpected, $acpi_nontestexpected) = @_;
# Note: shell script that rebuilds the expected files is in the same
# directory as files themselves.
# Note: allowed diff list can be changed both when changing expected
# files and when changing tests.
if ($name =~ m#^tests/data/acpi/# and not $name =~ m#^\.sh$#) {
$$acpi_testexpected = $name;
} elsif ($name !~ m#^tests/qtest/bios-tables-test-allowed-diff.h$#) {
$$acpi_nontestexpected = $name;
}
if (defined $$acpi_testexpected and defined $$acpi_nontestexpected) {
ERROR("Do not add expected files together with tests, " .
"follow instructions in " .
"tests/qtest/bios-tables-test.c: both " .
$$acpi_testexpected . " and " .
$$acpi_nontestexpected . " found\n");
}
}
sub checkspdx {
my ($file, $expr) = @_;
@ -1417,6 +1406,118 @@ sub checkspdx {
}
}
# All three of the methods below take a 'file info' record
# which is a hash ref containing
#
# 'isgit': 1 if an enhanced git diff or 0 for a plain diff
# 'githeader': 1 if still parsing git patch header, 0 otherwise
# 'linestart': line number of start of file diff
# 'lineend': line number of end of file diff
# 'filenew': the new filename
# 'fileold': the old filename (same as 'new filename' except
# for renames in git diffs)
# 'action': one of 'modified' (always) or 'new' or 'deleted' or
# 'renamed' (git diffs only)
# 'mode': file mode for new/deleted files (git diffs only)
# 'similarity': file similarity when renamed (git diffs only)
# 'facts': hash ref for storing any metadata related to checks
#
# Called at the end of each patch, with the list of
# real filenames that were seen in the patch
sub process_file_list {
my @fileinfos = @_;
# According to tests/qtest/bios-tables-test.c: do not
# change expected file in the same commit with adding test
my @acpi_testexpected;
my @acpi_nontestexpected;
foreach my $fileinfo (@fileinfos) {
# Note: shell script that rebuilds the expected files is in
# the same directory as files themselves.
# Note: allowed diff list can be changed both when changing
# expected files and when changing tests.
if ($fileinfo->{filenew} =~ m#^tests/data/acpi/# &&
$fileinfo->{filenew} !~ m#^\.sh$#) {
push @acpi_testexpected, $fileinfo->{filenew};
} elsif ($fileinfo->{filenew} !~
m#^tests/qtest/bios-tables-test-allowed-diff.h$#) {
push @acpi_nontestexpected, $fileinfo->{filenew};
}
}
if (int(@acpi_testexpected) > 0 and int(@acpi_nontestexpected) > 0) {
ERROR("Do not add expected files together with tests, " .
"follow instructions in " .
"tests/qtest/bios-tables-test.c. Files\n\n " .
join("\n ", @acpi_testexpected) .
"\n\nand\n\n " .
join("\n ", @acpi_nontestexpected) .
"\n\nfound in the same patch\n");
}
my $sawmaintainers = 0;
my @maybemaintainers;
foreach my $fileinfo (@fileinfos) {
if ($fileinfo->{action} ne "modified" &&
$fileinfo->{filenew} !~ m#^tests/data/acpi/#) {
push @maybemaintainers, $fileinfo->{filenew};
}
if ($fileinfo->{filenew} eq "MAINTAINERS") {
$sawmaintainers = 1;
}
}
# If we don't see a MAINTAINERS update, prod the user to check
if (int(@maybemaintainers) > 0 && !$sawmaintainers) {
WARN("added, moved or deleted file(s):\n\n " .
join("\n ", @maybemaintainers) .
"\n\nDoes MAINTAINERS need updating?\n");
}
}
# Called at the start of processing a diff hunk for a file
sub process_start_of_file {
my $fileinfo = shift;
# Check for incorrect file permissions
if ($fileinfo->{action} eq "new" && ($fileinfo->{mode} & 0111)) {
my $permhere = $fileinfo->{linestart} . "FILE: " .
$fileinfo->{filenew} . "\n";
if ($fileinfo->{filenew} =~
/(\bMakefile.*|\.(c|cc|cpp|h|mak|s|S))$/) {
ERROR("do not set execute permissions for source " .
"files\n" . $permhere);
}
}
}
# Called at the end of processing a diff hunk for a file
sub process_end_of_file {
my $fileinfo = shift;
if ($fileinfo->{action} eq "new" &&
!exists $fileinfo->{facts}->{sawspdx}) {
if ($fileinfo->{filenew} =~
/(\.(c|h|py|pl|sh|json|inc|rs)|Makefile.*)$/) {
# source code files MUST have SPDX license declared
ERROR("New file '" . $fileinfo->{filenew} .
"' requires 'SPDX-License-Identifier'");
} else {
# Other files MAY have SPDX license if appropriate
WARN("Does new file '" . $fileinfo->{filenew} .
"' need 'SPDX-License-Identifier'?");
}
}
if ($fileinfo->{action} eq "new" &&
exists $fileinfo->{facts}->{sawboilerplate}) {
ERROR("New file '" . $fileinfo->{filenew} . "' must " .
"not have license boilerplate header text, only " .
"the SPDX-License-Identifier, unless this file was " .
"copied from existing code already having such text.");
}
}
sub process {
my $filename = shift;
@ -1437,13 +1538,10 @@ sub process {
my $in_header_lines = $file ? 0 : 1;
my $in_commit_log = 0; #Scanning lines before patch
my $reported_maintainer_file = 0;
my $reported_mixing_imported_file = 0;
my $in_imported_file = 0;
my $in_no_imported_file = 0;
my $non_utf8_charset = 0;
my $expect_spdx = 0;
my $expect_spdx_file;
our @report = ();
our $cnt_lines = 0;
@ -1455,7 +1553,10 @@ sub process {
my $realfile = '';
my $realline = 0;
my $realcnt = 0;
my $fileinfo;
my @fileinfolist;
my $here = '';
my $oldhere = '';
my $in_comment = 0;
my $comment_edge = 0;
my $first_line = 0;
@ -1468,9 +1569,6 @@ sub process {
my %suppress_whiletrailers;
my %suppress_export;
my $acpi_testexpected;
my $acpi_nontestexpected;
# Pre-scan the patch sanitizing the lines.
sanitise_line_reset();
@ -1593,18 +1691,54 @@ sub process {
$prefix = "$filename:$realline: " if ($emacs && $file);
$prefix = "$filename:$linenr: " if ($emacs && !$file);
$oldhere = $here;
$here = "#$linenr: " if (!$file);
$here = "#$realline: " if ($file);
# extract the filename as it passes
if ($line =~ /^diff --git.*?(\S+)$/) {
$realfile = $1;
$realfile =~ s@^([^/]*)/@@ if (!$file);
checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected);
if ($line =~ /^diff --git\s+(\S+)\s+(\S+)$/) {
my $fileold = $1;
my $filenew = $2;
if (defined $fileinfo) {
$fileinfo->{lineend} = $oldhere;
process_end_of_file($fileinfo)
}
$fileold =~ s@^([^/]*)/@@ if (!$file);
$filenew =~ s@^([^/]*)/@@ if (!$file);
$realfile = $filenew;
$fileinfo = {
"isgit" => 1,
"githeader" => 1,
"linestart" => $here,
"lineend" => 0,
"fileold" => $fileold,
"filenew" => $filenew,
"action" => "modified",
"mode" => 0,
"similarity" => 0,
"facts" => {},
};
push @fileinfolist, $fileinfo;
} elsif (defined $fileinfo && $fileinfo->{githeader} &&
$line =~ /^(new|deleted) (?:file )?mode\s+([0-7]+)$/) {
$fileinfo->{action} = $1;
$fileinfo->{mode} = oct($2);
} elsif (defined $fileinfo && $fileinfo->{githeader} &&
$line =~ /^similarity index (\d+)%/) {
$fileinfo->{similarity} = int($1);
} elsif (defined $fileinfo && $fileinfo->{githeader} &&
$line =~ /^rename (from|to) [\w\/\.\-]+\s*$/) {
$fileinfo->{action} = "renamed";
# For a no-change rename, we'll never have any "+++..."
# lines, so trigger actions now
if ($1 eq "to" && $fileinfo->{similarity} == 100) {
process_start_of_file($fileinfo);
}
} elsif ($line =~ /^\+\+\+\s+(\S+)/) {
$realfile = $1;
$realfile =~ s@^([^/]*)/@@ if (!$file);
checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected);
$p1_prefix = $1;
if (!$file && $tree && $p1_prefix ne '' &&
@ -1612,6 +1746,30 @@ sub process {
WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
}
if (defined $fileinfo && !$fileinfo->{isgit}) {
$fileinfo->{lineend} = $oldhere;
process_end_of_file($fileinfo);
}
if (!defined $fileinfo || !$fileinfo->{isgit}) {
$fileinfo = {
"isgit" => 0,
"githeader" => 0,
"linestart" => $here,
"lineend" => 0,
"fileold" => $realfile,
"filenew" => $realfile,
"action" => "modified",
"mode" => 0,
"similarity" => 0,
"facts" => {},
};
push @fileinfolist, $fileinfo;
} else {
$fileinfo->{githeader} = 0;
}
process_start_of_file($fileinfo);
next;
}
@ -1623,14 +1781,6 @@ sub process {
$cnt_lines++ if ($realcnt != 0);
# Check for incorrect file permissions
if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
my $permhere = $here . "FILE: $realfile\n";
if ($realfile =~ /(\bMakefile(?:\.objs)?|\.c|\.cc|\.cpp|\.h|\.mak|\.[sS])$/) {
ERROR("do not set execute permissions for source files\n" . $permhere);
}
}
# Only allow Python 3 interpreter
if ($realline == 1 &&
$line =~ /^\+#!\ *\/usr\/bin\/(?:env )?python$/) {
@ -1662,68 +1812,27 @@ sub process {
}
}
# Check if MAINTAINERS is being updated. If so, there's probably no need to
# emit the "does MAINTAINERS need updating?" message on file add/move/delete
if ($line =~ /^\s*MAINTAINERS\s*\|/) {
$reported_maintainer_file = 1;
}
# Check for added, moved or deleted files
if (!$reported_maintainer_file && !$in_commit_log &&
($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ ||
$line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ ||
($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ &&
(defined($1) || defined($2)))) &&
!(($realfile ne '') &&
defined($acpi_testexpected) &&
($realfile eq $acpi_testexpected))) {
$reported_maintainer_file = 1;
WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
}
# All new files should have a SPDX-License-Identifier tag
if ($line =~ /^new file mode\s*\d+\s*$/) {
if ($expect_spdx) {
if ($expect_spdx_file =~
/\.(c|h|py|pl|sh|json|inc|Makefile)$/) {
# source code files MUST have SPDX license declared
ERROR("New file '$expect_spdx_file' requires " .
"'SPDX-License-Identifier'");
} else {
# Other files MAY have SPDX license if appropriate
WARN("Does new file '$expect_spdx_file' need " .
"'SPDX-License-Identifier'?");
}
}
$expect_spdx = 1;
$expect_spdx_file = undef;
} elsif ($expect_spdx) {
$expect_spdx_file = $realfile unless
defined $expect_spdx_file;
# SPDX tags may occurr in comments which were
# stripped from '$line', so use '$rawline'
if ($rawline =~ /SPDX-License-Identifier/) {
$expect_spdx = 0;
$expect_spdx_file = undef;
}
}
# Check SPDX-License-Identifier references a permitted license
if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) {
&checkspdx($realfile, $1);
$fileinfo->{facts}->{sawspdx} = 1;
&checkspdx($realfile, $1);
}
if ($rawline =~ /$LICENSE_BOILERPLATE_RE/) {
$fileinfo->{facts}->{sawboilerplate} = 1;
}
if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) {
my $tag = $1;
my @permitted = qw(
SPDX-License-Identifier
);
my $tag = $1;
my @permitted = qw(
SPDX-License-Identifier
);
unless (grep { /^$tag$/ } @permitted) {
ERROR("Tag $tag not permitted in QEMU code, valid " .
"choices are: " . join(", ", @permitted));
}
unless (grep { /^$tag$/ } @permitted) {
ERROR("Tag $tag not permitted in QEMU code, " .
"valid choices are: " .
join(", ", @permitted));
}
}
# Check for wrappage within a valid hunk of the file
@ -2304,7 +2413,7 @@ sub process {
# missing space after union, struct or enum definition
if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) {
ERROR("missing space after $1 definition\n" . $herecurr);
ERROR("missing space after $1 definition\n" . $herecurr);
}
# check for spacing round square brackets; allowed:
@ -2599,7 +2708,7 @@ sub process {
if ($line =~ /^.\s*(Q(?:S?LIST|SIMPLEQ|TAILQ)_HEAD)\s*\(\s*[^,]/ &&
$line !~ /^.typedef/) {
ERROR("named $1 should be typedefed separately\n" . $herecurr);
ERROR("named $1 should be typedefed separately\n" . $herecurr);
}
# Need a space before open parenthesis after if, while etc
@ -3148,48 +3257,50 @@ sub process {
# Qemu error function tests
# Find newlines in error messages
my $qemu_error_funcs = qr{error_setg|
error_setg_errno|
error_setg_win32|
error_setg_file_open|
error_set|
error_prepend|
warn_reportf_err|
error_reportf_err|
error_vreport|
warn_vreport|
info_vreport|
error_report|
warn_report|
info_report|
g_test_message}x;
# Find newlines in error messages
my $qemu_error_funcs = qr{error_setg|
error_setg_errno|
error_setg_win32|
error_setg_file_open|
error_set|
error_prepend|
warn_reportf_err|
error_reportf_err|
error_vreport|
warn_vreport|
info_vreport|
error_report|
warn_report|
info_report|
g_test_message}x;
if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) {
ERROR("Error messages should not contain newlines\n" . $herecurr);
}
# Continue checking for error messages that contains newlines. This
# check handles cases where string literals are spread over multiple lines.
# Example:
# error_report("Error msg line #1"
# "Error msg line #2\n");
my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"};
my $continued_str_literal = qr{\+\s*\".*\"};
if ($rawline =~ /$quoted_newline_regex/) {
# Backtrack to first line that does not contain only a quoted literal
# and assume that it is the start of the statement.
my $i = $linenr - 2;
while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) {
$i--;
}
if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) {
if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) {
ERROR("Error messages should not contain newlines\n" . $herecurr);
}
}
# Continue checking for error messages that contains newlines.
# This check handles cases where string literals are spread
# over multiple lines.
# Example:
# error_report("Error msg line #1"
# "Error msg line #2\n");
my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"};
my $continued_str_literal = qr{\+\s*\".*\"};
if ($rawline =~ /$quoted_newline_regex/) {
# Backtrack to first line that does not contain only
# a quoted literal and assume that it is the start
# of the statement.
my $i = $linenr - 2;
while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) {
$i--;
}
if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) {
ERROR("Error messages should not contain newlines\n" . $herecurr);
}
}
# check for non-portable libc calls that have portable alternatives in QEMU
if ($line =~ /\bffs\(/) {
@ -3243,6 +3354,11 @@ sub process {
}
}
if (defined $fileinfo) {
process_end_of_file($fileinfo);
}
process_file_list(@fileinfolist);
if ($is_patch && $chk_signoff && $signoff == 0) {
ERROR("Missing Signed-off-by: line(s)\n");
}

View file

@ -574,6 +574,13 @@ int main(int argc, char **argv)
for (i = 0; i < G_N_ELEMENTS(test_data); i++) {
if (test_data[i].open_opts->format == QCRYPTO_BLOCK_FORMAT_LUKS &&
!qcrypto_hash_supports(test_data[i].hash_alg)) {
g_printerr("# skip unsupported %s\n",
QCryptoHashAlgo_str(test_data[i].hash_alg));
continue;
}
if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128,
QCRYPTO_CIPHER_MODE_CBC)) {
g_printerr("# skip unsupported aes-128:cbc\n");
continue;
}
if (!test_data[i].slow ||

View file

@ -828,11 +828,16 @@ int main(int argc, char **argv)
}
}
g_test_add_func("/crypto/cipher/null-iv",
test_cipher_null_iv);
if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256,
QCRYPTO_CIPHER_MODE_CBC)) {
g_test_add_func("/crypto/cipher/null-iv",
test_cipher_null_iv);
g_test_add_func("/crypto/cipher/short-plaintext",
test_cipher_short_plaintext);
g_test_add_func("/crypto/cipher/short-plaintext",
test_cipher_short_plaintext);
} else {
g_printerr("# skip unsupported aes-256:cbc\n");
}
return g_test_run();
}

View file

@ -22,6 +22,7 @@
#include "crypto/init.h"
#include "crypto/secret.h"
#include "crypto/cipher.h"
#include "qapi/error.h"
#include "qemu/module.h"
#if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING)
@ -597,18 +598,21 @@ int main(int argc, char **argv)
g_test_add_func("/crypto/secret/conv/utf8/base64",
test_secret_conv_utf8_base64);
g_test_add_func("/crypto/secret/crypt/raw",
test_secret_crypt_raw);
g_test_add_func("/crypto/secret/crypt/base64",
test_secret_crypt_base64);
g_test_add_func("/crypto/secret/crypt/shortkey",
test_secret_crypt_short_key);
g_test_add_func("/crypto/secret/crypt/shortiv",
test_secret_crypt_short_iv);
g_test_add_func("/crypto/secret/crypt/missingiv",
test_secret_crypt_missing_iv);
g_test_add_func("/crypto/secret/crypt/badiv",
test_secret_crypt_bad_iv);
if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128,
QCRYPTO_CIPHER_MODE_CBC)) {
g_test_add_func("/crypto/secret/crypt/raw",
test_secret_crypt_raw);
g_test_add_func("/crypto/secret/crypt/base64",
test_secret_crypt_base64);
g_test_add_func("/crypto/secret/crypt/shortkey",
test_secret_crypt_short_key);
g_test_add_func("/crypto/secret/crypt/shortiv",
test_secret_crypt_short_iv);
g_test_add_func("/crypto/secret/crypt/missingiv",
test_secret_crypt_missing_iv);
g_test_add_func("/crypto/secret/crypt/badiv",
test_secret_crypt_bad_iv);
}
return g_test_run();
}

View file

@ -332,6 +332,216 @@ static void test_socket_unix_abstract(void)
#endif /* CONFIG_LINUX */
static void inet_parse_test_helper(const char *str,
InetSocketAddress *exp_addr, bool success)
{
InetSocketAddress addr;
Error *error = NULL;
int rc = inet_parse(&addr, str, &error);
if (success) {
g_assert_cmpint(rc, ==, 0);
} else {
g_assert_cmpint(rc, <, 0);
}
if (exp_addr != NULL) {
g_assert_cmpstr(addr.host, ==, exp_addr->host);
g_assert_cmpstr(addr.port, ==, exp_addr->port);
/* Own members: */
g_assert_cmpint(addr.has_numeric, ==, exp_addr->has_numeric);
g_assert_cmpint(addr.numeric, ==, exp_addr->numeric);
g_assert_cmpint(addr.has_to, ==, exp_addr->has_to);
g_assert_cmpint(addr.to, ==, exp_addr->to);
g_assert_cmpint(addr.has_ipv4, ==, exp_addr->has_ipv4);
g_assert_cmpint(addr.ipv4, ==, exp_addr->ipv4);
g_assert_cmpint(addr.has_ipv6, ==, exp_addr->has_ipv6);
g_assert_cmpint(addr.ipv6, ==, exp_addr->ipv6);
g_assert_cmpint(addr.has_keep_alive, ==, exp_addr->has_keep_alive);
g_assert_cmpint(addr.keep_alive, ==, exp_addr->keep_alive);
#ifdef HAVE_TCP_KEEPCNT
g_assert_cmpint(addr.has_keep_alive_count, ==,
exp_addr->has_keep_alive_count);
g_assert_cmpint(addr.keep_alive_count, ==,
exp_addr->keep_alive_count);
#endif
#ifdef HAVE_TCP_KEEPIDLE
g_assert_cmpint(addr.has_keep_alive_idle, ==,
exp_addr->has_keep_alive_idle);
g_assert_cmpint(addr.keep_alive_idle, ==,
exp_addr->keep_alive_idle);
#endif
#ifdef HAVE_TCP_KEEPINTVL
g_assert_cmpint(addr.has_keep_alive_interval, ==,
exp_addr->has_keep_alive_interval);
g_assert_cmpint(addr.keep_alive_interval, ==,
exp_addr->keep_alive_interval);
#endif
#ifdef HAVE_IPPROTO_MPTCP
g_assert_cmpint(addr.has_mptcp, ==, exp_addr->has_mptcp);
g_assert_cmpint(addr.mptcp, ==, exp_addr->mptcp);
#endif
}
g_free(addr.host);
g_free(addr.port);
}
static void test_inet_parse_nohost_good(void)
{
char host[] = "";
char port[] = "5000";
InetSocketAddress exp_addr = {
.host = host,
.port = port,
};
inet_parse_test_helper(":5000", &exp_addr, true);
}
static void test_inet_parse_empty_bad(void)
{
inet_parse_test_helper("", NULL, false);
}
static void test_inet_parse_only_colon_bad(void)
{
inet_parse_test_helper(":", NULL, false);
}
static void test_inet_parse_ipv4_good(void)
{
char host[] = "127.0.0.1";
char port[] = "5000";
InetSocketAddress exp_addr = {
.host = host,
.port = port,
};
inet_parse_test_helper("127.0.0.1:5000", &exp_addr, true);
}
static void test_inet_parse_ipv4_noport_bad(void)
{
inet_parse_test_helper("127.0.0.1", NULL, false);
}
static void test_inet_parse_ipv6_good(void)
{
char host[] = "::1";
char port[] = "5000";
InetSocketAddress exp_addr = {
.host = host,
.port = port,
};
inet_parse_test_helper("[::1]:5000", &exp_addr, true);
}
static void test_inet_parse_ipv6_noend_bad(void)
{
inet_parse_test_helper("[::1", NULL, false);
}
static void test_inet_parse_ipv6_noport_bad(void)
{
inet_parse_test_helper("[::1]:", NULL, false);
}
static void test_inet_parse_ipv6_empty_bad(void)
{
inet_parse_test_helper("[]:5000", NULL, false);
}
static void test_inet_parse_hostname_good(void)
{
char host[] = "localhost";
char port[] = "5000";
InetSocketAddress exp_addr = {
.host = host,
.port = port,
};
inet_parse_test_helper("localhost:5000", &exp_addr, true);
}
static void test_inet_parse_all_options_good(void)
{
char host[] = "::1";
char port[] = "5000";
InetSocketAddress exp_addr = {
.host = host,
.port = port,
.has_numeric = true,
.numeric = true,
.has_to = true,
.to = 5006,
.has_ipv4 = true,
.ipv4 = false,
.has_ipv6 = true,
.ipv6 = true,
.has_keep_alive = true,
.keep_alive = true,
#ifdef HAVE_TCP_KEEPCNT
.has_keep_alive_count = true,
.keep_alive_count = 10,
#endif
#ifdef HAVE_TCP_KEEPIDLE
.has_keep_alive_idle = true,
.keep_alive_idle = 60,
#endif
#ifdef HAVE_TCP_KEEPINTVL
.has_keep_alive_interval = true,
.keep_alive_interval = 30,
#endif
#ifdef HAVE_IPPROTO_MPTCP
.has_mptcp = true,
.mptcp = false,
#endif
};
inet_parse_test_helper(
"[::1]:5000,numeric=on,to=5006,ipv4=off,ipv6=on,keep-alive=on"
#ifdef HAVE_TCP_KEEPCNT
",keep-alive-count=10"
#endif
#ifdef HAVE_TCP_KEEPIDLE
",keep-alive-idle=60"
#endif
#ifdef HAVE_TCP_KEEPINTVL
",keep-alive-interval=30"
#endif
#ifdef HAVE_IPPROTO_MPTCP
",mptcp=off"
#endif
, &exp_addr, true);
}
static void test_inet_parse_all_implicit_bool_good(void)
{
char host[] = "::1";
char port[] = "5000";
InetSocketAddress exp_addr = {
.host = host,
.port = port,
.has_numeric = true,
.numeric = true,
.has_to = true,
.to = 5006,
.has_ipv4 = true,
.ipv4 = true,
.has_ipv6 = true,
.ipv6 = true,
.has_keep_alive = true,
.keep_alive = true,
#ifdef HAVE_IPPROTO_MPTCP
.has_mptcp = true,
.mptcp = true,
#endif
};
inet_parse_test_helper(
"[::1]:5000,numeric,to=5006,ipv4,ipv6,keep-alive"
#ifdef HAVE_IPPROTO_MPTCP
",mptcp"
#endif
, &exp_addr, true);
}
int main(int argc, char **argv)
{
bool has_ipv4, has_ipv6;
@ -377,6 +587,31 @@ int main(int argc, char **argv)
test_socket_unix_abstract);
#endif
g_test_add_func("/util/socket/inet-parse/nohost-good",
test_inet_parse_nohost_good);
g_test_add_func("/util/socket/inet-parse/empty-bad",
test_inet_parse_empty_bad);
g_test_add_func("/util/socket/inet-parse/only-colon-bad",
test_inet_parse_only_colon_bad);
g_test_add_func("/util/socket/inet-parse/ipv4-good",
test_inet_parse_ipv4_good);
g_test_add_func("/util/socket/inet-parse/ipv4-noport-bad",
test_inet_parse_ipv4_noport_bad);
g_test_add_func("/util/socket/inet-parse/ipv6-good",
test_inet_parse_ipv6_good);
g_test_add_func("/util/socket/inet-parse/ipv6-noend-bad",
test_inet_parse_ipv6_noend_bad);
g_test_add_func("/util/socket/inet-parse/ipv6-noport-bad",
test_inet_parse_ipv6_noport_bad);
g_test_add_func("/util/socket/inet-parse/ipv6-empty-bad",
test_inet_parse_ipv6_empty_bad);
g_test_add_func("/util/socket/inet-parse/hostname-good",
test_inet_parse_hostname_good);
g_test_add_func("/util/socket/inet-parse/all-options-good",
test_inet_parse_all_options_good);
g_test_add_func("/util/socket/inet-parse/all-bare-bool-good",
test_inet_parse_all_implicit_bool_good);
end:
return g_test_run();
}

View file

@ -126,33 +126,34 @@ uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman_format)
return 0;
}
int qemu_pixman_get_type(int rshift, int gshift, int bshift)
int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian)
{
int type = PIXMAN_TYPE_OTHER;
bool native_endian = (endian == G_BYTE_ORDER);
if (rshift > gshift && gshift > bshift) {
if (bshift == 0) {
type = PIXMAN_TYPE_ARGB;
type = native_endian ? PIXMAN_TYPE_ARGB : PIXMAN_TYPE_BGRA;
} else {
type = PIXMAN_TYPE_RGBA;
type = native_endian ? PIXMAN_TYPE_RGBA : PIXMAN_TYPE_ABGR;
}
} else if (rshift < gshift && gshift < bshift) {
if (rshift == 0) {
type = PIXMAN_TYPE_ABGR;
type = native_endian ? PIXMAN_TYPE_ABGR : PIXMAN_TYPE_RGBA;
} else {
type = PIXMAN_TYPE_BGRA;
type = native_endian ? PIXMAN_TYPE_BGRA : PIXMAN_TYPE_ARGB;
}
}
return type;
}
#ifdef CONFIG_PIXMAN
pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf)
pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian)
{
pixman_format_code_t format;
int type;
type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift);
type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift, endian);
format = PIXMAN_FORMAT(pf->bits_per_pixel, type,
pf->abits, pf->rbits, pf->gbits, pf->bbits);
if (!pixman_format_supported_source(format)) {

View file

@ -150,7 +150,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h)
* If client is big-endian, color samples begin from the second
* byte (offset 1) of a 32-bit pixel value.
*/
off = vs->client_be;
off = vs->client_endian == G_BIG_ENDIAN ? 1 : 0;
memset(stats, 0, sizeof (stats));
@ -891,7 +891,7 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
buf8 = buf;
if (1 /* FIXME */) {
if (vs->client_endian == G_BYTE_ORDER) {
rshift = vs->client_pf.rshift;
gshift = vs->client_pf.gshift;
bshift = vs->client_pf.bshift;
@ -1001,16 +1001,24 @@ static int send_mono_rect(VncState *vs, int x, int y,
break;
}
case 2:
vnc_write(vs, &bg, 2);
vnc_write(vs, &fg, 2);
{
uint16_t bg16 = bg;
uint16_t fg16 = fg;
vnc_write(vs, &bg16, 2);
vnc_write(vs, &fg16, 2);
tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg);
break;
}
default:
vnc_write_u8(vs, bg);
vnc_write_u8(vs, fg);
{
uint8_t bg8 = bg;
uint8_t fg8 = fg;
vnc_write_u8(vs, bg8);
vnc_write_u8(vs, fg8);
tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg);
break;
}
}
vs->tight->tight.offset = bytes;
bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);

View file

@ -255,7 +255,7 @@ static void zrle_write_u8(VncState *vs, uint8_t value)
static int zrle_send_framebuffer_update(VncState *vs, int x, int y,
int w, int h)
{
bool be = vs->client_be;
bool be = vs->client_endian == G_BIG_ENDIAN;
size_t bytes;
int zywrle_level;

View file

@ -188,7 +188,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
local->lossy_rect = orig->lossy_rect;
local->write_pixels = orig->write_pixels;
local->client_pf = orig->client_pf;
local->client_be = orig->client_be;
local->client_endian = orig->client_endian;
local->tight = orig->tight;
local->zlib = orig->zlib;
local->hextile = orig->hextile;

View file

@ -891,7 +891,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
buf[0] = v;
break;
case 2:
if (vs->client_be) {
if (vs->client_endian == G_BIG_ENDIAN) {
buf[0] = v >> 8;
buf[1] = v;
} else {
@ -901,7 +901,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
break;
default:
case 4:
if (vs->client_be) {
if (vs->client_endian == G_BIG_ENDIAN) {
buf[0] = v >> 24;
buf[1] = v >> 16;
buf[2] = v >> 8;
@ -2240,7 +2240,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
static void set_pixel_conversion(VncState *vs)
{
pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf);
pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf,
vs->client_endian);
if (fmt == VNC_SERVER_FB_FORMAT) {
vs->write_pixels = vnc_write_pixels_copy;
@ -2312,7 +2313,7 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel,
vs->client_pf.bits_per_pixel = bits_per_pixel;
vs->client_pf.bytes_per_pixel = bits_per_pixel / 8;
vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
vs->client_be = big_endian_flag;
vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
if (!true_color_flag) {
send_color_map(vs);

View file

@ -323,7 +323,7 @@ struct VncState
VncWritePixels *write_pixels;
PixelFormat client_pf;
pixman_format_code_t client_format;
bool client_be;
int client_endian; /* G_LITTLE_ENDIAN or G_BIG_ENDIAN */
CaptureVoiceOut *audio_cap;
struct audsettings as;

View file

@ -30,6 +30,7 @@
#include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h"
#include "qemu/cutils.h"
#include "qemu/option.h"
#include "trace.h"
#ifndef AI_ADDRCONFIG
@ -44,6 +45,14 @@
# define AI_NUMERICSERV 0
#endif
/*
* On macOS TCP_KEEPIDLE is available under a different name, TCP_KEEPALIVE.
* https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/man/man4/tcp.4#L172
*/
#if defined(TCP_KEEPALIVE) && !defined(TCP_KEEPIDLE)
# define TCP_KEEPIDLE TCP_KEEPALIVE
#endif
static int inet_getport(struct addrinfo *e)
{
@ -205,6 +214,58 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e)
#endif
}
static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp)
{
if (saddr->keep_alive) {
int keep_alive = 1;
int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
&keep_alive, sizeof(keep_alive));
if (ret < 0) {
error_setg_errno(errp, errno,
"Unable to set keep-alive option on socket");
return -1;
}
#ifdef HAVE_TCP_KEEPCNT
if (saddr->has_keep_alive_count && saddr->keep_alive_count) {
int keep_count = saddr->keep_alive_count;
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keep_count,
sizeof(keep_count));
if (ret < 0) {
error_setg_errno(errp, errno,
"Unable to set TCP keep-alive count option on socket");
return -1;
}
}
#endif
#ifdef HAVE_TCP_KEEPIDLE
if (saddr->has_keep_alive_idle && saddr->keep_alive_idle) {
int keep_idle = saddr->keep_alive_idle;
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle,
sizeof(keep_idle));
if (ret < 0) {
error_setg_errno(errp, errno,
"Unable to set TCP keep-alive idle option on socket");
return -1;
}
}
#endif
#ifdef HAVE_TCP_KEEPINTVL
if (saddr->has_keep_alive_interval && saddr->keep_alive_interval) {
int keep_interval = saddr->keep_alive_interval;
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keep_interval,
sizeof(keep_interval));
if (ret < 0) {
error_setg_errno(errp, errno,
"Unable to set TCP keep-alive interval option on socket");
return -1;
}
}
#endif
}
return 0;
}
static int inet_listen_saddr(InetSocketAddress *saddr,
int port_offset,
int num,
@ -220,12 +281,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
int saved_errno = 0;
bool socket_created = false;
if (saddr->keep_alive) {
error_setg(errp, "keep-alive option is not supported for passive "
"sockets");
return -1;
}
memset(&ai,0, sizeof(ai));
ai.ai_flags = AI_PASSIVE;
if (saddr->has_numeric && saddr->numeric) {
@ -287,11 +342,20 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
port_min = inet_getport(e);
port_max = saddr->has_to ? saddr->to + port_offset : port_min;
for (p = port_min; p <= port_max; p++) {
if (slisten >= 0) {
/*
* We have a socket we tried with the previous port. It cannot
* be rebound, we need to close it and create a new one.
*/
close(slisten);
slisten = -1;
}
inet_setport(e, p);
slisten = create_fast_reuse_socket(e);
if (slisten < 0) {
/* First time we expect we might fail to create the socket
/*
* First time we expect we might fail to create the socket
* eg if 'e' has AF_INET6 but ipv6 kmod is not loaded.
* Later iterations should always succeed if first iteration
* worked though, so treat that as fatal.
@ -301,40 +365,41 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
} else {
error_setg_errno(errp, errno,
"Failed to recreate failed listening socket");
goto listen_failed;
goto fail;
}
}
socket_created = true;
rc = try_bind(slisten, saddr, e);
if (rc < 0) {
if (errno != EADDRINUSE) {
error_setg_errno(errp, errno, "Failed to bind socket");
goto listen_failed;
}
} else {
if (!listen(slisten, num)) {
goto listen_ok;
}
if (errno != EADDRINUSE) {
error_setg_errno(errp, errno, "Failed to listen on socket");
goto listen_failed;
if (errno == EADDRINUSE) {
/* This port is already used, try the next one */
continue;
}
error_setg_errno(errp, errno, "Failed to bind socket");
goto fail;
}
/* Someone else managed to bind to the same port and beat us
* to listen on it! Socket semantics does not allow us to
* recover from this situation, so we need to recreate the
* socket to allow bind attempts for subsequent ports:
*/
close(slisten);
slisten = -1;
if (listen(slisten, num)) {
if (errno == EADDRINUSE) {
/* This port is already used, try the next one */
continue;
}
error_setg_errno(errp, errno, "Failed to listen on socket");
goto fail;
}
/* We have a listening socket */
if (inet_set_sockopts(slisten, saddr, errp) < 0) {
goto fail;
}
freeaddrinfo(res);
return slisten;
}
}
error_setg_errno(errp, errno,
socket_created ?
"Failed to find an available port" :
"Failed to create a socket");
listen_failed:
fail:
saved_errno = errno;
if (slisten >= 0) {
close(slisten);
@ -342,10 +407,6 @@ listen_failed:
freeaddrinfo(res);
errno = saved_errno;
return -1;
listen_ok:
freeaddrinfo(res);
return slisten;
}
#ifdef _WIN32
@ -475,16 +536,9 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp)
return sock;
}
if (saddr->keep_alive) {
int val = 1;
int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
&val, sizeof(val));
if (ret < 0) {
error_setg_errno(errp, errno, "Unable to set KEEPALIVE");
close(sock);
return -1;
}
if (inet_set_sockopts(sock, saddr, errp) < 0) {
close(sock);
return -1;
}
return sock;
@ -591,115 +645,140 @@ err:
return -1;
}
/* compatibility wrapper */
static int inet_parse_flag(const char *flagname, const char *optstr, bool *val,
Error **errp)
{
char *end;
size_t len;
end = strstr(optstr, ",");
if (end) {
if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */
error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr);
return -1;
}
len = end - optstr;
} else {
len = strlen(optstr);
}
if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) {
*val = true;
} else if (len == 4 && strncmp(optstr, "=off", len) == 0) {
*val = false;
} else {
error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr);
return -1;
}
return 0;
}
static QemuOptsList inet_opts = {
.name = "InetSocketAddress",
.head = QTAILQ_HEAD_INITIALIZER(inet_opts.head),
.implied_opt_name = "addr",
.desc = {
{
.name = "addr",
.type = QEMU_OPT_STRING,
},
{
.name = "numeric",
.type = QEMU_OPT_BOOL,
},
{
.name = "to",
.type = QEMU_OPT_NUMBER,
},
{
.name = "ipv4",
.type = QEMU_OPT_BOOL,
},
{
.name = "ipv6",
.type = QEMU_OPT_BOOL,
},
{
.name = "keep-alive",
.type = QEMU_OPT_BOOL,
},
#ifdef HAVE_TCP_KEEPCNT
{
.name = "keep-alive-count",
.type = QEMU_OPT_NUMBER,
},
#endif
#ifdef HAVE_TCP_KEEPIDLE
{
.name = "keep-alive-idle",
.type = QEMU_OPT_NUMBER,
},
#endif
#ifdef HAVE_TCP_KEEPINTVL
{
.name = "keep-alive-interval",
.type = QEMU_OPT_NUMBER,
},
#endif
#ifdef HAVE_IPPROTO_MPTCP
{
.name = "mptcp",
.type = QEMU_OPT_BOOL,
},
#endif
{ /* end of list */ }
},
};
int inet_parse(InetSocketAddress *addr, const char *str, Error **errp)
{
const char *optstr, *h;
char host[65];
char port[33];
int to;
int pos;
char *begin;
QemuOpts *opts = qemu_opts_parse(&inet_opts, str, true, errp);
if (!opts) {
return -1;
}
memset(addr, 0, sizeof(*addr));
/* parse address */
if (str[0] == ':') {
/* no host given */
host[0] = '\0';
if (sscanf(str, ":%32[^,]%n", port, &pos) != 1) {
error_setg(errp, "error parsing port in address '%s'", str);
return -1;
}
} else if (str[0] == '[') {
/* IPv6 addr */
if (sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos) != 2) {
error_setg(errp, "error parsing IPv6 address '%s'", str);
return -1;
}
} else {
/* hostname or IPv4 addr */
if (sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos) != 2) {
error_setg(errp, "error parsing address '%s'", str);
return -1;
}
const char *addr_str = qemu_opt_get(opts, "addr");
if (!addr_str) {
error_setg(errp, "error parsing address ''");
return -1;
}
if (str[0] == '[') {
/* IPv6 addr */
const char *ip_end = strstr(addr_str, "]:");
if (!ip_end || ip_end - addr_str < 2 || strlen(ip_end) < 3) {
error_setg(errp, "error parsing IPv6 address '%s'", addr_str);
return -1;
}
addr->host = g_strndup(addr_str + 1, ip_end - addr_str - 1);
addr->port = g_strdup(ip_end + 2);
} else {
/* no host, hostname or IPv4 addr */
const char *port = strchr(addr_str, ':');
if (!port || strlen(port) < 2) {
error_setg(errp, "error parsing address '%s'", addr_str);
return -1;
}
addr->host = g_strndup(addr_str, port - addr_str);
addr->port = g_strdup(port + 1);
}
addr->host = g_strdup(host);
addr->port = g_strdup(port);
/* parse options */
optstr = str + pos;
h = strstr(optstr, ",to=");
if (h) {
h += 4;
if (sscanf(h, "%d%n", &to, &pos) != 1 ||
(h[pos] != '\0' && h[pos] != ',')) {
error_setg(errp, "error parsing to= argument");
return -1;
}
if (qemu_opt_find(opts, "numeric")) {
addr->has_numeric = true,
addr->numeric = qemu_opt_get_bool(opts, "numeric", false);
}
if (qemu_opt_find(opts, "to")) {
addr->has_to = true;
addr->to = to;
addr->to = qemu_opt_get_number(opts, "to", 0);
}
begin = strstr(optstr, ",ipv4");
if (begin) {
if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) {
return -1;
}
if (qemu_opt_find(opts, "ipv4")) {
addr->has_ipv4 = true;
addr->ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
}
begin = strstr(optstr, ",ipv6");
if (begin) {
if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) {
return -1;
}
if (qemu_opt_find(opts, "ipv6")) {
addr->has_ipv6 = true;
addr->ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
}
begin = strstr(optstr, ",keep-alive");
if (begin) {
if (inet_parse_flag("keep-alive", begin + strlen(",keep-alive"),
&addr->keep_alive, errp) < 0)
{
return -1;
}
if (qemu_opt_find(opts, "keep-alive")) {
addr->has_keep_alive = true;
addr->keep_alive = qemu_opt_get_bool(opts, "keep-alive", false);
}
#ifdef HAVE_TCP_KEEPCNT
if (qemu_opt_find(opts, "keep-alive-count")) {
addr->has_keep_alive_count = true;
addr->keep_alive_count = qemu_opt_get_number(opts, "keep-alive-count", 0);
}
#endif
#ifdef HAVE_TCP_KEEPIDLE
if (qemu_opt_find(opts, "keep-alive-idle")) {
addr->has_keep_alive_idle = true;
addr->keep_alive_idle = qemu_opt_get_number(opts, "keep-alive-idle", 0);
}
#endif
#ifdef HAVE_TCP_KEEPINTVL
if (qemu_opt_find(opts, "keep-alive-interval")) {
addr->has_keep_alive_interval = true;
addr->keep_alive_interval = qemu_opt_get_number(opts, "keep-alive-interval", 0);
}
#endif
#ifdef HAVE_IPPROTO_MPTCP
begin = strstr(optstr, ",mptcp");
if (begin) {
if (inet_parse_flag("mptcp", begin + strlen(",mptcp"),
&addr->mptcp, errp) < 0)
{
return -1;
}
if (qemu_opt_find(opts, "mptcp")) {
addr->has_mptcp = true;
addr->mptcp = qemu_opt_get_bool(opts, "mptcp", 0);
}
#endif
return 0;