validate and normalize received CDR data

The CDR deserializer failed to check it was staying within the bounds of
the received data, and it turns out it also was inconsistent in its
interpretation of the (undocumented) serializer instructions.  This
commit adds some information on the instruction format obtained by
reverse engineering the code and studying the output of the IDL
preprocessor, and furthermore changes a lot of the types used in the
(de)serializer code to have some more compiler support.  The IDL
preprocessor is untouched and the generated instructinos do exactly the
same thing (except where change was needed).

The bulk of this commit replaces the implementation of the
(de)serializer.  It is still rather ugly, but at least the very long
functions with several levels of nested conditions and switch statements
have been split out into multiple functions.  Most of these have single
call-sites, so the compiler hopefully inlines them nicely.

The other important thing is that it adds a "normalize" function that
validates the structure of the CDR and performs byteswapping if
necessary.  This means the deserializer can now assume a well-formed
input in native byte-order.  Checks and conditional byteswaps have been
removed accordingly.

It changes some types to make a compile-time distinction between
read-only, native-endianness input, a native-endianness output, and a
big-endian output for dealing with key hashes.  This should reduce the
risk of accidentally mixing endianness or modifying an input stream.

The preprocessor has been modified to indicate the presence of unions in
a topic type in the descriptor flags.  If a union is present, any
memory allocated in a sample is freed first and the sample is zero'd out
prior to deserializing the new value.  This is to prevent reading
garbage pointers for strings and sequences when switching union cases.

The test tool has been included in the commit but it does not get run by
itself.  Firstly, it requires the presence of OpenSplice DDS as an
alternative implementation to check the CDR processing against.
Secondly, it takes quite a while to run and is of no interest unless one
changes something in the (de)serialization.

Finally, I have no idea why there was a "CDR stream" interface among the
public functions.  The existing interfaces are fundamentally broken by
the removal of arbitrary-endianness streams, and the interfaces were
already incapable of proper error notification.  So, they have been
removed.

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2019-05-10 17:59:06 +08:00 committed by eboasson
parent d91e7b34c9
commit 3067a69c92
25 changed files with 2315 additions and 1941 deletions

View file

@ -45,7 +45,6 @@ typedef int32_t dds_entity_t;
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/retcode.h"
#include "dds/ddsrt/log.h"
#include "dds/ddsc/dds_public_stream.h"
#include "dds/ddsc/dds_public_impl.h"
#include "dds/ddsc/dds_public_alloc.h"
#include "dds/ddsc/dds_public_qos.h"

View file

@ -21,9 +21,10 @@
#ifndef DDS_IMPL_H
#define DDS_IMPL_H
#include <stdint.h>
#include <stdbool.h>
#include "dds/export.h"
#include "dds/ddsc/dds_public_alloc.h"
#include "dds/ddsc/dds_public_stream.h"
#if defined (__cplusplus)
extern "C" {
@ -71,6 +72,7 @@ dds_topic_descriptor_t;
#define DDS_TOPIC_NO_OPTIMIZE 0x0001
#define DDS_TOPIC_FIXED_KEY 0x0002
#define DDS_TOPIC_CONTAINS_UNION 0x0004
/*
Masks for read condition, read, take: there is only one mask here,
@ -119,89 +121,119 @@ typedef int32_t dds_domainid_t;
/* Topic encoding instruction types */
#define DDS_OP_RTS 0x00000000
#define DDS_OP_ADR 0x01000000
#define DDS_OP_JSR 0x02000000
#define DDS_OP_JEQ 0x03000000
enum dds_stream_opcode {
/* return from subroutine, exits top-level
[RTS, 0, 0, 0] */
DDS_OP_RTS = 0x00 << 24,
/* data field
[ADR, nBY, 0, k] [offset]
[ADR, STR, 0, k] [offset]
[ADR, BST, 0, k] [offset] [bound]
[ADR, SEQ, nBY, 0] [offset]
[ADR, SEQ, STR, 0] [offset]
[ADR, SEQ, BST, 0] [offset] [bound]
[ADR, SEQ, s, 0] [offset] [elem-size] [next-insn, elem-insn]
where s = {SEQ,ARR,UNI,STU}
[ADR, ARR, nBY, k] [offset] [alen]
[ADR, ARR, STR, 0] [offset] [alen]
[ADR, ARR, BST, 0] [offset] [alen] [0] [bound]
[ADR, ARR, s, 0] [offset] [alen] [next-insn, elem-insn] [elem-size]
where s = {SEQ,ARR,UNI,STU}
[ADR, UNI, d, z] [offset] [alen] [next-insn, cases]
where
d = discriminant type of {1BY,2BY,4BY}
z = default present/not present (DDS_OP_FLAG_DEF)
offset = discriminant offset
followed by alen case labels: in JEQ format
note: [ADR, STU, ...] is illegal
where
s = subtype
k = key/not key (DDS_OP_FLAG_KEY)
[offset] = field offset from start of element in memory
[elem-size] = element size in memory
[bound] = string bound + 1
[alen] = array length, number of cases
[next-insn] = (unsigned 16 bits) offset to instruction for next field, from start of insn
[elem-insn] = (unsigned 16 bits) offset to first instruction for element, from start of insn
[cases] = (unsigned 16 bits) offset to first case label, from start of insn
*/
DDS_OP_ADR = 0x01 << 24,
/* jump-to-subroutine (apparently not used at the moment)
[JSR, 0, e]
where
e = (signed 16 bits) offset to first instruction in subroutine, from start of insn
instruction sequence must end in RTS, execution resumes at instruction
following JSR */
DDS_OP_JSR = 0x02 << 24,
/* union case
[JEQ, nBY, 0] [disc] [offset]
[JEQ, STR, 0] [disc] [offset]
[JEQ, s, e] [disc] [offset]
where
s = subtype other than {nBY,STR}
e = (unsigned 16 bits) offset to first instruction for case, from start of insn
instruction sequence must end in RTS, at which point executes continues
at the next field's instruction as specified by the union */
DDS_OP_JEQ = 0x03 << 24
};
/* Core type flags
1BY : One byte simple type
2BY : Two byte simple type
4BY : Four byte simple type
8BY : Eight byte simple type
STR : String
BST : Bounded string
SEQ : Sequence
ARR : Array
UNI : Union
STU : Struct
*/
#define DDS_OP_VAL_1BY 0x01
#define DDS_OP_VAL_2BY 0x02
#define DDS_OP_VAL_4BY 0x03
#define DDS_OP_VAL_8BY 0x04
#define DDS_OP_VAL_STR 0x05
#define DDS_OP_VAL_BST 0x06
#define DDS_OP_VAL_SEQ 0x07
#define DDS_OP_VAL_ARR 0x08
#define DDS_OP_VAL_UNI 0x09
#define DDS_OP_VAL_STU 0x0a
#define DDS_OP_TYPE_1BY (DDS_OP_VAL_1BY << 16)
#define DDS_OP_TYPE_2BY (DDS_OP_VAL_2BY << 16)
#define DDS_OP_TYPE_4BY (DDS_OP_VAL_4BY << 16)
#define DDS_OP_TYPE_8BY (DDS_OP_VAL_8BY << 16)
#define DDS_OP_TYPE_STR (DDS_OP_VAL_STR << 16)
#define DDS_OP_TYPE_SEQ (DDS_OP_VAL_SEQ << 16)
#define DDS_OP_TYPE_ARR (DDS_OP_VAL_ARR << 16)
#define DDS_OP_TYPE_UNI (DDS_OP_VAL_UNI << 16)
#define DDS_OP_TYPE_STU (DDS_OP_VAL_STU << 16)
#define DDS_OP_TYPE_BST (DDS_OP_VAL_BST << 16)
enum dds_stream_typecode {
DDS_OP_VAL_1BY = 0x01, /* one byte simple type (char, octet, boolean) */
DDS_OP_VAL_2BY = 0x02, /* two byte simple type ((unsigned) short) */
DDS_OP_VAL_4BY = 0x03, /* four byte simple type ((unsigned) long, enums, float) */
DDS_OP_VAL_8BY = 0x04, /* eight byte simple type ((unsigned) long long, double) */
DDS_OP_VAL_STR = 0x05, /* string */
DDS_OP_VAL_BST = 0x06, /* bounded string */
DDS_OP_VAL_SEQ = 0x07, /* sequence */
DDS_OP_VAL_ARR = 0x08, /* array */
DDS_OP_VAL_UNI = 0x09, /* union */
DDS_OP_VAL_STU = 0x0a /* struct */
};
/* primary type code for DDS_OP_ADR, DDS_OP_JEQ */
enum dds_stream_typecode_primary {
DDS_OP_TYPE_1BY = DDS_OP_VAL_1BY << 16,
DDS_OP_TYPE_2BY = DDS_OP_VAL_2BY << 16,
DDS_OP_TYPE_4BY = DDS_OP_VAL_4BY << 16,
DDS_OP_TYPE_8BY = DDS_OP_VAL_8BY << 16,
DDS_OP_TYPE_STR = DDS_OP_VAL_STR << 16,
DDS_OP_TYPE_BST = DDS_OP_VAL_BST << 16,
DDS_OP_TYPE_SEQ = DDS_OP_VAL_SEQ << 16,
DDS_OP_TYPE_ARR = DDS_OP_VAL_ARR << 16,
DDS_OP_TYPE_UNI = DDS_OP_VAL_UNI << 16,
DDS_OP_TYPE_STU = DDS_OP_VAL_STU << 16
};
#define DDS_OP_TYPE_BOO DDS_OP_TYPE_1BY
/* sub-type code:
- encodes element type for DDS_OP_TYPE_{SEQ,ARR},
- discriminant type for DDS_OP_TYPE_UNI */
enum dds_stream_typecode_subtype {
DDS_OP_SUBTYPE_1BY = DDS_OP_VAL_1BY << 8,
DDS_OP_SUBTYPE_2BY = DDS_OP_VAL_2BY << 8,
DDS_OP_SUBTYPE_4BY = DDS_OP_VAL_4BY << 8,
DDS_OP_SUBTYPE_8BY = DDS_OP_VAL_8BY << 8,
DDS_OP_SUBTYPE_STR = DDS_OP_VAL_STR << 8,
DDS_OP_SUBTYPE_BST = DDS_OP_VAL_BST << 8,
DDS_OP_SUBTYPE_SEQ = DDS_OP_VAL_SEQ << 8,
DDS_OP_SUBTYPE_ARR = DDS_OP_VAL_ARR << 8,
DDS_OP_SUBTYPE_UNI = DDS_OP_VAL_UNI << 8,
DDS_OP_SUBTYPE_STU = DDS_OP_VAL_STU << 8
};
#define DDS_OP_SUBTYPE_BOO DDS_OP_SUBTYPE_1BY
#define DDS_OP_SUBTYPE_1BY (DDS_OP_VAL_1BY << 8)
#define DDS_OP_SUBTYPE_2BY (DDS_OP_VAL_2BY << 8)
#define DDS_OP_SUBTYPE_4BY (DDS_OP_VAL_4BY << 8)
#define DDS_OP_SUBTYPE_8BY (DDS_OP_VAL_8BY << 8)
#define DDS_OP_SUBTYPE_STR (DDS_OP_VAL_STR << 8)
#define DDS_OP_SUBTYPE_SEQ (DDS_OP_VAL_SEQ << 8)
#define DDS_OP_SUBTYPE_ARR (DDS_OP_VAL_ARR << 8)
#define DDS_OP_SUBTYPE_UNI (DDS_OP_VAL_UNI << 8)
#define DDS_OP_SUBTYPE_STU (DDS_OP_VAL_STU << 8)
#define DDS_OP_SUBTYPE_BST (DDS_OP_VAL_BST << 8)
#define DDS_OP_FLAG_KEY 0x01
#define DDS_OP_FLAG_DEF 0x02
#define DDS_OP_FLAG_KEY 0x01 /* key field: applicable to {1,2,4,8}BY, STR, BST, ARR-of-{1,2,4,8}BY */
#define DDS_OP_FLAG_DEF 0x02 /* union has a default case (for DDS_OP_ADR | DDS_OP_TYPE_UNI) */
/**
* Description : Enable or disable write batching. Overrides default configuration
* setting for write batching (DDSI2E/Internal/WriteBatch).
* setting for write batching (Internal/WriteBatch).
*
* Arguments :
* -# enable Enables or disables write batching for all writers.
*/
DDS_EXPORT void dds_write_set_batch (bool enable);
/**
* Description : Install tcp/ssl and encryption support. Depends on openssl.
*
* Arguments :
* -# None
*/
DDS_EXPORT void dds_ssl_plugin (void);
/**
* Description : Install client durability support. Depends on OSPL server.
*
* Arguments :
* -# None
*/
DDS_EXPORT void dds_durability_plugin (void);
#if defined (__cplusplus)
}
#endif

View file

@ -1,108 +0,0 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
/** @file
*
* @brief DDS C Stream API
*
* This header file defines the public API of the Streams in the
* Eclipse Cyclone DDS C language binding.
*/
#ifndef DDS_STREAM_H
#define DDS_STREAM_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "dds/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
struct dds_sequence;
typedef union
{
uint8_t * p8;
uint16_t * p16;
uint32_t * p32;
uint64_t * p64;
float * pf;
double * pd;
void * pv;
}
dds_uptr_t;
typedef struct dds_stream
{
dds_uptr_t m_buffer; /* Union of pointers to start of buffer */
uint32_t m_size; /* Buffer size */
uint32_t m_index; /* Read/write offset from start of buffer */
bool m_endian; /* Endian: big (false) or little (true) */
bool m_failed; /* Attempt made to read beyond end of buffer */
}
dds_stream_t;
#define DDS_STREAM_BE false
#define DDS_STREAM_LE true
DDS_EXPORT dds_stream_t * dds_stream_create (uint32_t size);
DDS_EXPORT dds_stream_t * dds_stream_from_buffer (const void *buf, size_t sz, int bswap);
DDS_EXPORT void dds_stream_delete (dds_stream_t * st);
DDS_EXPORT void dds_stream_fini (dds_stream_t * st);
DDS_EXPORT void dds_stream_reset (dds_stream_t * st);
DDS_EXPORT void dds_stream_init (dds_stream_t * st, uint32_t size);
DDS_EXPORT void dds_stream_grow (dds_stream_t * st, uint32_t size);
DDS_EXPORT bool dds_stream_endian (void);
struct dds_topic_descriptor;
DDS_EXPORT void dds_stream_read_sample_w_desc (dds_stream_t * is, void * data, const struct dds_topic_descriptor * desc);
DDS_EXPORT bool dds_stream_read_bool (dds_stream_t * is);
DDS_EXPORT uint8_t dds_stream_read_uint8 (dds_stream_t * is);
DDS_EXPORT uint16_t dds_stream_read_uint16 (dds_stream_t * is);
DDS_EXPORT uint32_t dds_stream_read_uint32 (dds_stream_t * is);
DDS_EXPORT uint64_t dds_stream_read_uint64 (dds_stream_t * is);
DDS_EXPORT float dds_stream_read_float (dds_stream_t * is);
DDS_EXPORT double dds_stream_read_double (dds_stream_t * is);
DDS_EXPORT char * dds_stream_read_string (dds_stream_t * is);
DDS_EXPORT void dds_stream_read_buffer (dds_stream_t * is, uint8_t * buffer, uint32_t len);
inline char dds_stream_read_char (dds_stream_t *is) { return (char) dds_stream_read_uint8 (is); }
inline int8_t dds_stream_read_int8 (dds_stream_t *is) { return (int8_t) dds_stream_read_uint8 (is); }
inline int16_t dds_stream_read_int16 (dds_stream_t *is) { return (int16_t) dds_stream_read_uint16 (is); }
inline int32_t dds_stream_read_int32 (dds_stream_t *is) { return (int32_t) dds_stream_read_uint32 (is); }
inline int64_t dds_stream_read_int64 (dds_stream_t *is) { return (int64_t) dds_stream_read_uint64 (is); }
DDS_EXPORT void dds_stream_write_bool (dds_stream_t * os, bool val);
DDS_EXPORT void dds_stream_write_uint8 (dds_stream_t * os, uint8_t val);
DDS_EXPORT void dds_stream_write_uint16 (dds_stream_t * os, uint16_t val);
DDS_EXPORT void dds_stream_write_uint32 (dds_stream_t * os, uint32_t val);
DDS_EXPORT void dds_stream_write_uint64 (dds_stream_t * os, uint64_t val);
DDS_EXPORT void dds_stream_write_float (dds_stream_t * os, float val);
DDS_EXPORT void dds_stream_write_double (dds_stream_t * os, double val);
DDS_EXPORT void dds_stream_write_string (dds_stream_t * os, const char * val);
DDS_EXPORT void dds_stream_write_buffer (dds_stream_t * os, uint32_t len, const uint8_t * buffer);
DDS_EXPORT void *dds_stream_address (dds_stream_t * s);
DDS_EXPORT void *dds_stream_alignto (dds_stream_t * s, uint32_t a);
inline void dds_stream_write_char (dds_stream_t * os, char val) { dds_stream_write_uint8 (os, (uint8_t) val); }
inline void dds_stream_write_int8 (dds_stream_t * os, int8_t val) { dds_stream_write_uint8 (os, (uint8_t) val); }
inline void dds_stream_write_int16 (dds_stream_t * os, int16_t val) { dds_stream_write_uint16 (os, (uint16_t) val); }
inline void dds_stream_write_int32 (dds_stream_t * os, int32_t val) { dds_stream_write_uint32 (os, (uint32_t) val); }
inline void dds_stream_write_int64 (dds_stream_t * os, int64_t val) { dds_stream_write_uint64 (os, (uint64_t) val); }
#if defined (__cplusplus)
}
#endif
#endif