1cd62a92dSRobert Mustacchi /*
2cd62a92dSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3cd62a92dSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4cd62a92dSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5cd62a92dSRobert Mustacchi  * 1.0 of the CDDL.
6cd62a92dSRobert Mustacchi  *
7cd62a92dSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8cd62a92dSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9cd62a92dSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10cd62a92dSRobert Mustacchi  */
11cd62a92dSRobert Mustacchi 
12cd62a92dSRobert Mustacchi /*
13cd62a92dSRobert Mustacchi  * Copyright 2020 Robert Mustacchi
14cd62a92dSRobert Mustacchi  */
15cd62a92dSRobert Mustacchi 
16cd62a92dSRobert Mustacchi /*
17cd62a92dSRobert Mustacchi  * Implements open_memstream(3C).
18cd62a92dSRobert Mustacchi  */
19cd62a92dSRobert Mustacchi 
20cd62a92dSRobert Mustacchi #include "mtlib.h"
21cd62a92dSRobert Mustacchi #include "file64.h"
22cd62a92dSRobert Mustacchi #include <stdio.h>
23cd62a92dSRobert Mustacchi #include "stdiom.h"
24cd62a92dSRobert Mustacchi #include <errno.h>
25cd62a92dSRobert Mustacchi #include <stdlib.h>
26cd62a92dSRobert Mustacchi #include <fcntl.h>
27cd62a92dSRobert Mustacchi #include <sys/sysmacros.h>
28cd62a92dSRobert Mustacchi #include <limits.h>
29cd62a92dSRobert Mustacchi 
30cd62a92dSRobert Mustacchi typedef struct memstream {
31cd62a92dSRobert Mustacchi 	char *mstr_buf;
32cd62a92dSRobert Mustacchi 	size_t mstr_alloc;
33cd62a92dSRobert Mustacchi 	size_t mstr_pos;
34cd62a92dSRobert Mustacchi 	size_t mstr_lsize;
35cd62a92dSRobert Mustacchi 	char **mstr_ubufp;
36cd62a92dSRobert Mustacchi 	size_t *mstr_usizep;
37cd62a92dSRobert Mustacchi } memstream_t;
38cd62a92dSRobert Mustacchi 
39cd62a92dSRobert Mustacchi /*
40cd62a92dSRobert Mustacchi  * Common seek and overflow detection logic for the memory stream family of
41cd62a92dSRobert Mustacchi  * functions (open_memstream, open_wmemstream, etc.). We need to validate
42cd62a92dSRobert Mustacchi  * several things:
43cd62a92dSRobert Mustacchi  *
44cd62a92dSRobert Mustacchi  *  - That the offset when applied to base doesn't cause an over or underflow.
45cd62a92dSRobert Mustacchi  *  - That the resulting offset is positive (done implicitly with the above)
46cd62a92dSRobert Mustacchi  *  - That the resulting offset does not exceed an off_t's maximum size.
47cd62a92dSRobert Mustacchi  *    Unfortunately the kernel doesn't export an OFF_MAX value to userland, so
48cd62a92dSRobert Mustacchi  *    we have to know that it will always be equivalent to the environment's
49cd62a92dSRobert Mustacchi  *    long. This is designed with the assumption that in an ILP32 environment we
50cd62a92dSRobert Mustacchi  *    care about an off_t and not an off64_t. In cases where an off64_t is
51cd62a92dSRobert Mustacchi  *    valid, we still have to fit inside of the size_t constraints.
52cd62a92dSRobert Mustacchi  *
53cd62a92dSRobert Mustacchi  * We check for each of the cases and only perform unsigned arithmetic to verify
54cd62a92dSRobert Mustacchi  * that we have defined behavior.
55cd62a92dSRobert Mustacchi  */
56cd62a92dSRobert Mustacchi boolean_t
memstream_seek(size_t base,off_t off,size_t max,size_t * nposp)57cd62a92dSRobert Mustacchi memstream_seek(size_t base, off_t off, size_t max, size_t *nposp)
58cd62a92dSRobert Mustacchi {
59cd62a92dSRobert Mustacchi 	size_t npos;
60cd62a92dSRobert Mustacchi 
61cd62a92dSRobert Mustacchi 	npos = base + (size_t)off;
62cd62a92dSRobert Mustacchi 	if (off >= 0 && npos < base) {
63cd62a92dSRobert Mustacchi 		return (B_FALSE);
64cd62a92dSRobert Mustacchi 	}
65cd62a92dSRobert Mustacchi 
66cd62a92dSRobert Mustacchi 	if (off >= 0 && npos > LONG_MAX) {
67cd62a92dSRobert Mustacchi 		return (B_FALSE);
68cd62a92dSRobert Mustacchi 	}
69cd62a92dSRobert Mustacchi 
70cd62a92dSRobert Mustacchi 	if (off < 0 && npos >= base) {
71cd62a92dSRobert Mustacchi 		return (B_FALSE);
72cd62a92dSRobert Mustacchi 	}
73cd62a92dSRobert Mustacchi 
74cd62a92dSRobert Mustacchi 	if (npos > max) {
75cd62a92dSRobert Mustacchi 		return (B_FALSE);
76cd62a92dSRobert Mustacchi 	}
77cd62a92dSRobert Mustacchi 
78cd62a92dSRobert Mustacchi 	*nposp = npos;
79cd62a92dSRobert Mustacchi 	return (B_TRUE);
80cd62a92dSRobert Mustacchi }
81cd62a92dSRobert Mustacchi 
82cd62a92dSRobert Mustacchi int
memstream_newsize(size_t pos,size_t alloc,size_t nbytes,size_t * nallocp)83cd62a92dSRobert Mustacchi memstream_newsize(size_t pos, size_t alloc, size_t nbytes, size_t *nallocp)
84cd62a92dSRobert Mustacchi {
85cd62a92dSRobert Mustacchi 	size_t npos = pos + nbytes + 1;
86cd62a92dSRobert Mustacchi 	if (npos < pos) {
87cd62a92dSRobert Mustacchi 		/*
88cd62a92dSRobert Mustacchi 		 * We've been asked to write a number of bytes that would result
89cd62a92dSRobert Mustacchi 		 * in an overflow in the position. This means the stream would
90cd62a92dSRobert Mustacchi 		 * need to allocate all of memory, that's impractical.
91cd62a92dSRobert Mustacchi 		 */
92cd62a92dSRobert Mustacchi 		errno = EOVERFLOW;
93cd62a92dSRobert Mustacchi 		return (-1);
94cd62a92dSRobert Mustacchi 	}
95cd62a92dSRobert Mustacchi 
96cd62a92dSRobert Mustacchi 	/*
97cd62a92dSRobert Mustacchi 	 * If the new position is beyond the allocated amount, grow the array to
98cd62a92dSRobert Mustacchi 	 * a practical amount.
99cd62a92dSRobert Mustacchi 	 */
100cd62a92dSRobert Mustacchi 	if (npos > alloc) {
101cd62a92dSRobert Mustacchi 		size_t newalloc = P2ROUNDUP(npos, BUFSIZ);
102cd62a92dSRobert Mustacchi 		if (newalloc < npos) {
103cd62a92dSRobert Mustacchi 			errno = EOVERFLOW;
104cd62a92dSRobert Mustacchi 			return (-1);
105cd62a92dSRobert Mustacchi 		}
106cd62a92dSRobert Mustacchi 		*nallocp = newalloc;
107cd62a92dSRobert Mustacchi 		return (1);
108cd62a92dSRobert Mustacchi 	}
109cd62a92dSRobert Mustacchi 
110cd62a92dSRobert Mustacchi 	return (0);
111cd62a92dSRobert Mustacchi }
112cd62a92dSRobert Mustacchi 
113cd62a92dSRobert Mustacchi /*
114cd62a92dSRobert Mustacchi  * The SUSv4 spec says that this should not support reads.
115cd62a92dSRobert Mustacchi  */
116cd62a92dSRobert Mustacchi static ssize_t
open_memstream_read(FILE * iop __unused,char * buf __unused,size_t nbytes __unused)117*4a38094cSToomas Soome open_memstream_read(FILE *iop __unused, char *buf __unused,
118*4a38094cSToomas Soome     size_t nbytes __unused)
119cd62a92dSRobert Mustacchi {
120cd62a92dSRobert Mustacchi 	errno = EBADF;
121cd62a92dSRobert Mustacchi 	return (-1);
122cd62a92dSRobert Mustacchi }
123cd62a92dSRobert Mustacchi 
124cd62a92dSRobert Mustacchi static ssize_t
open_memstream_write(FILE * iop,const char * buf,size_t nbytes)125cd62a92dSRobert Mustacchi open_memstream_write(FILE *iop, const char *buf, size_t nbytes)
126cd62a92dSRobert Mustacchi {
127cd62a92dSRobert Mustacchi 	memstream_t *memp = _xdata(iop);
128cd62a92dSRobert Mustacchi 	size_t newsize;
129cd62a92dSRobert Mustacchi 	int ret;
130cd62a92dSRobert Mustacchi 
131cd62a92dSRobert Mustacchi 	/*
132cd62a92dSRobert Mustacchi 	 * We need to fit inside of an ssize_t, so we need to first constrain
133cd62a92dSRobert Mustacchi 	 * nbytes to a reasonable value.
134cd62a92dSRobert Mustacchi 	 */
135cd62a92dSRobert Mustacchi 	nbytes = MIN(nbytes, SSIZE_MAX);
136cd62a92dSRobert Mustacchi 	ret = memstream_newsize(memp->mstr_pos, memp->mstr_alloc, nbytes,
137cd62a92dSRobert Mustacchi 	    &newsize);
138cd62a92dSRobert Mustacchi 	if (ret < 0) {
139cd62a92dSRobert Mustacchi 		return (-1);
140cd62a92dSRobert Mustacchi 	} else if (ret > 0) {
141cd62a92dSRobert Mustacchi 		void *temp;
142cd62a92dSRobert Mustacchi 		temp = recallocarray(memp->mstr_buf, memp->mstr_alloc,
143cd62a92dSRobert Mustacchi 		    newsize, sizeof (char));
144cd62a92dSRobert Mustacchi 		if (temp == NULL) {
145cd62a92dSRobert Mustacchi 			return (-1);
146cd62a92dSRobert Mustacchi 		}
147cd62a92dSRobert Mustacchi 		memp->mstr_buf = temp;
148cd62a92dSRobert Mustacchi 		memp->mstr_alloc = newsize;
149cd62a92dSRobert Mustacchi 		*memp->mstr_ubufp = temp;
150cd62a92dSRobert Mustacchi 	}
151cd62a92dSRobert Mustacchi 
152cd62a92dSRobert Mustacchi 	(void) memcpy(&memp->mstr_buf[memp->mstr_pos], buf, nbytes);
153cd62a92dSRobert Mustacchi 	memp->mstr_pos += nbytes;
154cd62a92dSRobert Mustacchi 
155cd62a92dSRobert Mustacchi 	if (memp->mstr_pos > memp->mstr_lsize) {
156cd62a92dSRobert Mustacchi 		memp->mstr_lsize = memp->mstr_pos;
157cd62a92dSRobert Mustacchi 		memp->mstr_buf[memp->mstr_pos] = '\0';
158cd62a92dSRobert Mustacchi 	}
159cd62a92dSRobert Mustacchi 	*memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
160cd62a92dSRobert Mustacchi 
161cd62a92dSRobert Mustacchi 	return (nbytes);
162cd62a92dSRobert Mustacchi }
163cd62a92dSRobert Mustacchi 
164cd62a92dSRobert Mustacchi static off_t
open_memstream_seek(FILE * iop,off_t off,int whence)165cd62a92dSRobert Mustacchi open_memstream_seek(FILE *iop, off_t off, int whence)
166cd62a92dSRobert Mustacchi {
167cd62a92dSRobert Mustacchi 	memstream_t *memp = _xdata(iop);
168cd62a92dSRobert Mustacchi 	size_t base, npos;
169cd62a92dSRobert Mustacchi 
170cd62a92dSRobert Mustacchi 	switch (whence) {
171cd62a92dSRobert Mustacchi 	case SEEK_SET:
172cd62a92dSRobert Mustacchi 		base = 0;
173cd62a92dSRobert Mustacchi 		break;
174cd62a92dSRobert Mustacchi 	case SEEK_CUR:
175cd62a92dSRobert Mustacchi 		base = memp->mstr_pos;
176cd62a92dSRobert Mustacchi 		break;
177cd62a92dSRobert Mustacchi 	case SEEK_END:
178cd62a92dSRobert Mustacchi 		base = memp->mstr_lsize;
179cd62a92dSRobert Mustacchi 		break;
180cd62a92dSRobert Mustacchi 	default:
181cd62a92dSRobert Mustacchi 		errno = EINVAL;
182cd62a92dSRobert Mustacchi 		return (-1);
183cd62a92dSRobert Mustacchi 	}
184cd62a92dSRobert Mustacchi 
185cd62a92dSRobert Mustacchi 	if (!memstream_seek(base, off, SSIZE_MAX, &npos)) {
186cd62a92dSRobert Mustacchi 		errno = EINVAL;
187cd62a92dSRobert Mustacchi 		return (-1);
188cd62a92dSRobert Mustacchi 	}
189cd62a92dSRobert Mustacchi 	memp->mstr_pos = npos;
190cd62a92dSRobert Mustacchi 	*memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
191cd62a92dSRobert Mustacchi 
192cd62a92dSRobert Mustacchi 	return ((off_t)memp->mstr_pos);
193cd62a92dSRobert Mustacchi }
194cd62a92dSRobert Mustacchi 
195cd62a92dSRobert Mustacchi static int
open_memstream_close(FILE * iop)196cd62a92dSRobert Mustacchi open_memstream_close(FILE *iop)
197cd62a92dSRobert Mustacchi {
198cd62a92dSRobert Mustacchi 	memstream_t *memp = _xdata(iop);
199cd62a92dSRobert Mustacchi 	free(memp);
200cd62a92dSRobert Mustacchi 	_xunassoc(iop);
201cd62a92dSRobert Mustacchi 	return (0);
202cd62a92dSRobert Mustacchi }
203cd62a92dSRobert Mustacchi 
204cd62a92dSRobert Mustacchi FILE *
open_memstream(char ** bufp,size_t * sizep)205cd62a92dSRobert Mustacchi open_memstream(char **bufp, size_t *sizep)
206cd62a92dSRobert Mustacchi {
207cd62a92dSRobert Mustacchi 	FILE *iop;
208cd62a92dSRobert Mustacchi 	memstream_t *memp;
209cd62a92dSRobert Mustacchi 
210cd62a92dSRobert Mustacchi 	if (bufp == NULL || sizep == NULL) {
211cd62a92dSRobert Mustacchi 		errno = EINVAL;
212cd62a92dSRobert Mustacchi 		return (NULL);
213cd62a92dSRobert Mustacchi 	}
214cd62a92dSRobert Mustacchi 
215cd62a92dSRobert Mustacchi 	memp = calloc(1, sizeof (memstream_t));
216cd62a92dSRobert Mustacchi 	if (memp == NULL) {
217cd62a92dSRobert Mustacchi 		return (NULL);
218cd62a92dSRobert Mustacchi 	}
219cd62a92dSRobert Mustacchi 
220cd62a92dSRobert Mustacchi 	memp->mstr_alloc = BUFSIZ;
221cd62a92dSRobert Mustacchi 	memp->mstr_buf = calloc(memp->mstr_alloc, sizeof (char));
222cd62a92dSRobert Mustacchi 	if (memp->mstr_buf == NULL) {
223cd62a92dSRobert Mustacchi 		goto cleanup;
224cd62a92dSRobert Mustacchi 	}
225cd62a92dSRobert Mustacchi 	memp->mstr_buf[0] = '\0';
226cd62a92dSRobert Mustacchi 	memp->mstr_pos = 0;
227cd62a92dSRobert Mustacchi 	memp->mstr_lsize = 0;
228cd62a92dSRobert Mustacchi 	memp->mstr_ubufp = bufp;
229cd62a92dSRobert Mustacchi 	memp->mstr_usizep = sizep;
230cd62a92dSRobert Mustacchi 
231cd62a92dSRobert Mustacchi 	iop = _findiop();
232cd62a92dSRobert Mustacchi 	if (iop == NULL) {
233cd62a92dSRobert Mustacchi 		goto cleanup;
234cd62a92dSRobert Mustacchi 	}
235cd62a92dSRobert Mustacchi 
236cd62a92dSRobert Mustacchi #ifdef	_LP64
237cd62a92dSRobert Mustacchi 	iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | _IOWRT;
238cd62a92dSRobert Mustacchi #else
239cd62a92dSRobert Mustacchi 	iop->_flag = _IOWRT;
240cd62a92dSRobert Mustacchi #endif
241cd62a92dSRobert Mustacchi 
242cd62a92dSRobert Mustacchi 	/*
243cd62a92dSRobert Mustacchi 	 * Update the user pointers now, in case a call to fflush() happens
244cd62a92dSRobert Mustacchi 	 * immediately.
245cd62a92dSRobert Mustacchi 	 */
246cd62a92dSRobert Mustacchi 
247cd62a92dSRobert Mustacchi 	if (_xassoc(iop, open_memstream_read, open_memstream_write,
248cd62a92dSRobert Mustacchi 	    open_memstream_seek, open_memstream_close, memp) != 0) {
249cd62a92dSRobert Mustacchi 		goto cleanup;
250cd62a92dSRobert Mustacchi 	}
251cd62a92dSRobert Mustacchi 	_setorientation(iop, _BYTE_MODE);
252cd62a92dSRobert Mustacchi 	SET_SEEKABLE(iop);
253cd62a92dSRobert Mustacchi 
254cd62a92dSRobert Mustacchi 	*memp->mstr_ubufp = memp->mstr_buf;
255cd62a92dSRobert Mustacchi 	*memp->mstr_usizep = MIN(memp->mstr_pos, memp->mstr_lsize);
256cd62a92dSRobert Mustacchi 
257cd62a92dSRobert Mustacchi 	return (iop);
258cd62a92dSRobert Mustacchi 
259cd62a92dSRobert Mustacchi cleanup:
260cd62a92dSRobert Mustacchi 	free(memp->mstr_buf);
261cd62a92dSRobert Mustacchi 	free(memp);
262cd62a92dSRobert Mustacchi 	return (NULL);
263cd62a92dSRobert Mustacchi }
264