Skip to content

Commit db3b965

Browse files
authored
Merge pull request ceph#60159 from rzarzynski/wip-denc-ctcheck-struct_v
dencoding: check struct_v against DECODE_START(v, ...) at compile-time Reviewed-by: Ilya Dryomov <[email protected]> Reviewed-by: Greg Farnum <[email protected]> Reviewed-by: Jesse Williamson <[email protected]> Reviewed-by: Adam Kupczyk <[email protected]> Reviewed-by: Nitzan Mordechai <[email protected]>
2 parents bf517e0 + f983d75 commit db3b965

File tree

21 files changed

+160
-38
lines changed

21 files changed

+160
-38
lines changed

src/common/versioned_variant.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ void decode(std::variant<Ts...>& v, bufferlist::const_iterator& p)
203203

204204
// use struct_v as an index into the variant after converting it into a
205205
// compile-time index I
206-
const uint8_t index = struct_v - converted_max_version;
206+
const uint8_t index = struct_v.v - converted_max_version;
207207
boost::mp11::mp_with_index<sizeof...(Ts)>(index, [&v, &p] (auto I) {
208208
// default-construct the type at index I and call its decoder
209209
decode(v.template emplace<I>(), p);

src/include/ceph_assert.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,10 @@ using namespace ceph;
144144
? _CEPH_ASSERT_VOID_CAST (0) \
145145
: ::ceph::__ceph_assertf_fail (__STRING(expr), __FILE__, __LINE__, __CEPH_ASSERT_FUNCTION, __VA_ARGS__))
146146

147+
#define consteval_assert(expr, msg) \
148+
do { \
149+
if (!(expr)) { \
150+
throw (msg); \
151+
} \
152+
} while(false)
147153
#endif

src/include/denc.h

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,42 @@ inline std::enable_if_t<traits::supported && !traits::featured> decode_nohead(
17901790
"' v=" + std::to_string(code_v)+ " cannot decode v=" + std::to_string(struct_v) +
17911791
" minimal_decoder=" + std::to_string(struct_compat));
17921792
}
1793+
1794+
// compile-time checker for struct_v to detect mismatch of declared
1795+
// decoder version with actually implemented blocks like "struct_v < 100".
1796+
// it addresses the common problem of forgetting to bump the version up
1797+
// in decoder's `DECODE_START` (or `DENC_START`) when adding a new
1798+
// schema revision.
1799+
template <__u8 MaxV>
1800+
struct StructVChecker
1801+
{
1802+
struct CheckingWrapper {
1803+
consteval CheckingWrapper(__u8 c) : c(c) {
1804+
consteval_assert(
1805+
c <= MaxV,
1806+
"checking against higher version than declared in DECODE_START");
1807+
}
1808+
__u8 c;
1809+
};
1810+
// we want the implicit conversion to take place but with lower
1811+
// rank than the CheckingWrapper-conversion during struct_v cmps.
1812+
template<class T=void> operator __u8() {
1813+
return v;
1814+
}
1815+
// need the wrapper as the operator cannot be consteval; otherwise
1816+
// it couldn't have accessed the non-constexpr `v`.
1817+
auto operator<=>(CheckingWrapper c) {
1818+
return v <=> c.c;
1819+
}
1820+
auto operator==(CheckingWrapper c) {
1821+
return v == c.c;
1822+
}
1823+
auto operator!=(CheckingWrapper c) {
1824+
return v != c.c;
1825+
}
1826+
__u8 v;
1827+
};
1828+
17931829
#define DENC_HELPERS \
17941830
/* bound_encode */ \
17951831
static void _denc_start(size_t& p, \
@@ -1857,39 +1893,39 @@ inline std::enable_if_t<traits::supported && !traits::featured> decode_nohead(
18571893
// Due to -2 compatibility rule we cannot bump up compat until U____ release.
18581894
// SQUID=19 T____=20 U____=21
18591895

1860-
#define DENC_START(v, compat, p) \
1861-
__u8 struct_v = v; \
1896+
#define DENC_START(_v, compat, p) \
1897+
StructVChecker<_v> struct_v{_v}; \
18621898
__u8 struct_compat = compat; \
18631899
char *_denc_pchar; \
18641900
uint32_t _denc_u32; \
18651901
static_assert(CEPH_RELEASE >= (CEPH_RELEASE_SQUID /*19*/ + 2) || compat == 1); \
1866-
_denc_start(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
1902+
_denc_start(p, &struct_v.v, &struct_compat, &_denc_pchar, &_denc_u32); \
18671903
do {
18681904

18691905
// For the only type that is with compat 2: unittest.
1870-
#define DENC_START_COMPAT_2(v, compat, p) \
1871-
__u8 struct_v = v; \
1906+
#define DENC_START_COMPAT_2(_v, compat, p) \
1907+
StructVChecker<_v> struct_v{_v}; \
18721908
__u8 struct_compat = compat; \
18731909
char *_denc_pchar; \
18741910
uint32_t _denc_u32; \
18751911
static_assert(CEPH_RELEASE >= (CEPH_RELEASE_SQUID /*19*/ + 2) || compat == 2); \
1876-
_denc_start(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
1912+
_denc_start(p, &struct_v.v, &struct_compat, &_denc_pchar, &_denc_u32); \
18771913
do {
18781914

18791915
// For osd_reqid_t which cannot be upgraded at all.
18801916
// We used it to communicate with clients and now we cannot safely upgrade.
1881-
#define DENC_START_OSD_REQID(v, compat, p) \
1882-
__u8 struct_v = v; \
1917+
#define DENC_START_OSD_REQID(_v, compat, p) \
1918+
StructVChecker<_v> struct_v{_v}; \
18831919
__u8 struct_compat = compat; \
18841920
char *_denc_pchar; \
18851921
uint32_t _denc_u32; \
18861922
static_assert(compat == 2, "osd_reqid_t cannot be upgraded"); \
1887-
_denc_start(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32); \
1923+
_denc_start(p, &struct_v.v, &struct_compat, &_denc_pchar, &_denc_u32); \
18881924
do {
18891925

18901926
#define DENC_FINISH(p) \
18911927
} while (false); \
1892-
_denc_finish(p, &struct_v, &struct_compat, &_denc_pchar, &_denc_u32);
1928+
_denc_finish(p, &struct_v.v, &struct_compat, &_denc_pchar, &_denc_u32);
18931929

18941930

18951931
// ----------------------------------------------------------------------

src/include/encoding.h

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,22 @@ decode(std::array<T, N>& v, bufferlist::const_iterator& p)
14931493
* @param v current version of the encoding that the code supports/encodes
14941494
* @param bl bufferlist::iterator for the encoded data
14951495
*/
1496-
#define DECODE_START(v, bl) \
1496+
#define DECODE_START(_v, bl) \
1497+
StructVChecker<_v> struct_v; \
1498+
__u8 struct_compat; \
1499+
using ::ceph::decode; \
1500+
decode(struct_v.v, bl); \
1501+
decode(struct_compat, bl); \
1502+
if (_v < struct_compat) \
1503+
throw ::ceph::buffer::malformed_input(DECODE_ERR_NO_COMPAT(__PRETTY_FUNCTION__, _v, struct_v.v, struct_compat)); \
1504+
__u32 struct_len; \
1505+
decode(struct_len, bl); \
1506+
if (struct_len > bl.get_remaining()) \
1507+
throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
1508+
unsigned struct_end = bl.get_off() + struct_len; \
1509+
do {
1510+
1511+
#define DECODE_START_UNCHECKED(v, bl) \
14971512
__u8 struct_v, struct_compat; \
14981513
using ::ceph::decode; \
14991514
decode(struct_v, bl); \
@@ -1527,22 +1542,22 @@ decode(std::array<T, N>& v, bufferlist::const_iterator& p)
15271542

15281543
/* BEWARE: any change to this macro MUST be also reflected in the duplicative
15291544
* DECODE_START_LEGACY_COMPAT_LEN! */
1530-
#define __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, skip_v, bl) \
1545+
#define __DECODE_START_LEGACY_COMPAT_LEN(_v, compatv, lenv, skip_v, bl) \
15311546
using ::ceph::decode; \
1532-
__u8 struct_v; \
1533-
decode(struct_v, bl); \
1534-
if (struct_v >= compatv) { \
1547+
StructVChecker<_v> struct_v; \
1548+
decode(struct_v.v, bl); \
1549+
if (struct_v.v >= compatv) { \
15351550
__u8 struct_compat; \
15361551
decode(struct_compat, bl); \
1537-
if (v < struct_compat) \
1538-
throw ::ceph::buffer::malformed_input(DECODE_ERR_NO_COMPAT(__PRETTY_FUNCTION__, v, struct_v, struct_compat)); \
1552+
if (_v < struct_compat) \
1553+
throw ::ceph::buffer::malformed_input(DECODE_ERR_NO_COMPAT(__PRETTY_FUNCTION__, _v, struct_v.v, struct_compat)); \
15391554
} else if (skip_v) { \
15401555
if (bl.get_remaining() < skip_v) \
15411556
throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \
15421557
bl += skip_v; \
15431558
} \
15441559
unsigned struct_end = 0; \
1545-
if (struct_v >= lenv) { \
1560+
if (struct_v.v >= lenv) { \
15461561
__u32 struct_len; \
15471562
decode(struct_len, bl); \
15481563
if (struct_len > bl.get_remaining()) \

src/librbd/journal/Types.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ void EventEntry::encode(bufferlist& bl) const {
422422
}
423423

424424
void EventEntry::decode(bufferlist::const_iterator& it) {
425-
DECODE_START(1, it);
425+
DECODE_START(5, it);
426426

427427
uint32_t event_type;
428428
decode(event_type, it);

src/mds/CInode.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1771,7 +1771,7 @@ void CInode::decode_lock_ilink(bufferlist::const_iterator& p)
17711771
{
17721772
ceph_assert(!is_auth());
17731773
auto _inode = allocate_inode(*get_inode());
1774-
DECODE_START(1, p);
1774+
DECODE_START(2, p);
17751775
decode(_inode->version, p);
17761776
utime_t tm;
17771777
decode(tm, p);

src/mds/FSMap.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ void FSMap::decode(bufferlist::const_iterator& p)
664664
struct_version = 0;
665665
DECODE_START(STRUCT_VERSION, p);
666666
DECODE_OLDEST(7);
667-
struct_version = struct_v;
667+
struct_version = struct_v.v;
668668
decode(epoch, p);
669669
decode(next_filesystem_id, p);
670670
decode(legacy_client_fscid, p);

src/mds/MDCache.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11214,7 +11214,7 @@ void MDCache::decode_replica_dir(CDir *&dir, bufferlist::const_iterator& p, CIno
1121411214

1121511215
void MDCache::decode_replica_dentry(CDentry *&dn, bufferlist::const_iterator& p, CDir *dir, MDSContext::vec& finished)
1121611216
{
11217-
DECODE_START(1, p);
11217+
DECODE_START(3, p);
1121811218
string name;
1121911219
snapid_t last;
1122011220
decode(name, p);
@@ -11490,7 +11490,7 @@ void MDCache::encode_remote_dentry_link(CDentry::linkage_t *dnl, bufferlist& bl)
1149011490

1149111491
void MDCache::decode_remote_dentry_link(CDir *dir, CDentry *dn, bufferlist::const_iterator& p)
1149211492
{
11493-
DECODE_START(1, p);
11493+
DECODE_START(2, p);
1149411494
inodeno_t ino;
1149511495
__u8 d_type;
1149611496
decode(ino, p);

src/messages/MClientReply.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ struct InodeStat {
166166
void decode(ceph::buffer::list::const_iterator &p, const uint64_t features) {
167167
using ceph::decode;
168168
if (features == (uint64_t)-1) {
169-
DECODE_START(7, p);
169+
DECODE_START(8, p);
170170
decode(vino.ino, p);
171171
decode(vino.snapid, p);
172172
decode(rdev, p);

src/mon/MgrMap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class MgrMap
146146
}
147147

148148
void decode(ceph::buffer::list::const_iterator &bl) {
149-
DECODE_START(1, bl);
149+
DECODE_START(2, bl);
150150
decode(name, bl);
151151
decode(can_run, bl);
152152
decode(error_string, bl);

0 commit comments

Comments
 (0)