From 74eb0017be0e89df7c0ce0a18a7042dc7d06bfdb Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 9 Dec 2004 07:05:00 +0000 Subject: r4110: fixed pidl to allow arrays to have size_is() and length_is() elements that depend on variables that come after the array in the structure or function. This has been something that has been problematic for a while, but the winreg QueryValue problem finally prompted me to fix it properly. We should now go back and fix up all the ugly workarounds we have used to avoid this problem in other calls. Unfortunately the solution is fairly complex, and involves the use of the internal ndr token lists (similar to the solution for relative pointers). I wonder if anyone else will be able to follow the logic if I get run over by a bus :-) (This used to be commit e839b19ec5581f669f2a7705b1fb80845313251c) --- source4/librpc/ndr/libndr.h | 27 ++++------ source4/librpc/ndr/ndr.c | 118 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 110 insertions(+), 35 deletions(-) (limited to 'source4/librpc/ndr') diff --git a/source4/librpc/ndr/libndr.h b/source4/librpc/ndr/libndr.h index fe817bfdcd..bb28c3a23c 100644 --- a/source4/librpc/ndr/libndr.h +++ b/source4/librpc/ndr/libndr.h @@ -47,6 +47,8 @@ struct ndr_pull { uint32_t offset; struct ndr_token_list *relative_list; + struct ndr_token_list *array_size_list; + struct ndr_token_list *array_length_list; /* this is used to ensure we generate unique reference IDs between request and reply */ @@ -149,7 +151,8 @@ enum ndr_err_code { NDR_ERR_VALIDATE, NDR_ERR_BUFSIZE, NDR_ERR_ALLOC, - NDR_ERR_RANGE + NDR_ERR_RANGE, + NDR_ERR_TOKEN }; /* @@ -236,30 +239,20 @@ enum ndr_err_code { #define NDR_ALLOC_N_SIZE(ndr, s, n, elsize) do { \ - if ((n) == 0) { \ - (s) = NULL; \ - } else { \ - (s) = talloc_array(ndr, elsize, n, __location__); \ - if (!(s)) return ndr_pull_error(ndr, \ - NDR_ERR_ALLOC, \ - "Alloc %u * %u failed\n", \ - n, elsize); \ - } \ - } while (0) + (s) = talloc_array(ndr, elsize, n, __location__); \ + if (!(s)) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "Alloc %u * %u failed\n", n, elsize); \ +} while (0) #define NDR_ALLOC_N(ndr, s, n) NDR_ALLOC_N_SIZE(ndr, s, n, sizeof(*(s))) #define NDR_PUSH_ALLOC_SIZE(ndr, s, size) do { \ - (s) = talloc(ndr, size); \ - if ((size) && !(s)) return ndr_push_error(ndr, NDR_ERR_ALLOC, \ - "push alloc %u failed\n",\ - size); \ - } while (0) + (s) = talloc(ndr, size); \ + if (!(s)) return ndr_push_error(ndr, NDR_ERR_ALLOC, "push alloc %u failed\n", size); \ +} while (0) #define NDR_PUSH_ALLOC(ndr, s) NDR_PUSH_ALLOC_SIZE(ndr, s, sizeof(*(s))) - /* these are used when generic fn pointers are needed for ndr push/pull fns */ typedef NTSTATUS (*ndr_push_fn_t)(struct ndr_push *, void *); typedef NTSTATUS (*ndr_pull_fn_t)(struct ndr_pull *, void *); diff --git a/source4/librpc/ndr/ndr.c b/source4/librpc/ndr/ndr.c index 857e171224..7b55f4efb7 100644 --- a/source4/librpc/ndr/ndr.c +++ b/source4/librpc/ndr/ndr.c @@ -48,15 +48,11 @@ struct ndr_pull *ndr_pull_init_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx) { struct ndr_pull *ndr; - ndr = talloc_p(mem_ctx, struct ndr_pull); + ndr = talloc_zero_p(mem_ctx, struct ndr_pull); if (!ndr) return NULL; - ndr->flags = 0; ndr->data = blob->data; ndr->data_size = blob->length; - ndr->offset = 0; - ndr->ptr_count = 0; - ndr->relative_list = NULL; return ndr; } @@ -125,7 +121,7 @@ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx) { struct ndr_push *ndr; - ndr = talloc_p(mem_ctx, struct ndr_push); + ndr = talloc_zero_p(mem_ctx, struct ndr_push); if (!ndr) { return NULL; } @@ -136,9 +132,6 @@ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx) if (!ndr->data) { return NULL; } - ndr->offset = 0; - ndr->ptr_count = 0; - ndr->relative_list = NULL; return ndr; } @@ -381,8 +374,12 @@ static NTSTATUS ndr_map_error(enum ndr_err_code err) switch (err) { case NDR_ERR_BUFSIZE: return NT_STATUS_BUFFER_TOO_SMALL; + case NDR_ERR_TOKEN: + return NT_STATUS_INTERNAL_ERROR; case NDR_ERR_ALLOC: return NT_STATUS_NO_MEMORY; + case NDR_ERR_ARRAY_SIZE: + return NT_STATUS_ARRAY_BOUNDS_EXCEEDED; default: break; } @@ -655,18 +652,109 @@ static NTSTATUS ndr_token_store(TALLOC_CTX *mem_ctx, /* retrieve a token from a ndr context */ -static uint32_t ndr_token_retrieve(struct ndr_token_list **list, const void *key) +static NTSTATUS ndr_token_retrieve(struct ndr_token_list **list, const void *key, uint32_t *v) { struct ndr_token_list *tok; for (tok=*list;tok;tok=tok->next) { if (tok->key == key) { DLIST_REMOVE((*list), tok); + *v = tok->value; + return NT_STATUS_OK; + } + } + return ndr_map_error(NDR_ERR_TOKEN); +} + +/* + peek at but don't removed a token from a ndr context +*/ +static uint32_t ndr_token_peek(struct ndr_token_list **list, const void *key) +{ + struct ndr_token_list *tok; + for (tok=*list;tok;tok=tok->next) { + if (tok->key == key) { return tok->value; } } return 0; } +/* + pull an array size field and add it to the array_size_list token list +*/ +NTSTATUS ndr_pull_array_size(struct ndr_pull *ndr, const void *p) +{ + uint32_t size; + NDR_CHECK(ndr_pull_uint32(ndr, &size)); + return ndr_token_store(ndr, &ndr->array_size_list, p, size); +} + +/* + get the stored array size field +*/ +uint32_t ndr_get_array_size(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->array_size_list, p); +} + +/* + check the stored array size field +*/ +NTSTATUS ndr_check_array_size(struct ndr_pull *ndr, const void **p, uint32_t size) +{ + uint32 stored; + if (*p == NULL) { + return NT_STATUS_OK; + } + NDR_CHECK(ndr_token_retrieve(&ndr->array_size_list, p, &stored)); + if (stored != size) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "Bad array size - got %u expected %u\n", + stored, size); + } + return NT_STATUS_OK; +} + +/* + pull an array length field and add it to the array_length_list token list +*/ +NTSTATUS ndr_pull_array_length(struct ndr_pull *ndr, const void *p) +{ + uint32_t length, offset; + NDR_CHECK(ndr_pull_uint32(ndr, &offset)); + if (offset != 0) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "non-zero array offset %u\n", offset); + } + NDR_CHECK(ndr_pull_uint32(ndr, &length)); + return ndr_token_store(ndr, &ndr->array_length_list, p, length); +} + +/* + get the stored array length field +*/ +uint32_t ndr_get_array_length(struct ndr_pull *ndr, const void *p) +{ + return ndr_token_peek(&ndr->array_length_list, p); +} + +/* + check the stored array length field +*/ +NTSTATUS ndr_check_array_length(struct ndr_pull *ndr, void *p, uint32_t length) +{ + uint32_t stored; + if (*(void **)p == NULL) { + return NT_STATUS_OK; + } + NDR_CHECK(ndr_token_retrieve(&ndr->array_length_list, p, &stored)); + if (stored != length) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, + "Bad array length - got %u expected %u\n", + stored, length); + } + return NT_STATUS_OK; +} /* pull a relative object - stage1 @@ -689,10 +777,7 @@ NTSTATUS ndr_pull_relative1(struct ndr_pull *ndr, const void *p, uint32_t rel_of NTSTATUS ndr_pull_relative2(struct ndr_pull *ndr, const void *p) { uint32_t rel_offset; - rel_offset = ndr_token_retrieve(&ndr->relative_list, p); - if (rel_offset == 0) { - return NT_STATUS_INTERNAL_ERROR; - } + NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &rel_offset)); return ndr_pull_set_offset(ndr, rel_offset); } @@ -723,10 +808,7 @@ NTSTATUS ndr_push_relative2(struct ndr_push *ndr, const void *p) } NDR_CHECK(ndr_push_align(ndr, 4)); ndr_push_save(ndr, &save); - ndr->offset = ndr_token_retrieve(&ndr->relative_list, p); - if (ndr->offset == 0) { - return NT_STATUS_INTERNAL_ERROR; - } + NDR_CHECK(ndr_token_retrieve(&ndr->relative_list, p, &ndr->offset)); if (ndr->flags & LIBNDR_FLAG_RELATIVE_CURRENT) { NDR_CHECK(ndr_push_uint32(ndr, save.offset - ndr->offset)); } else { -- cgit