Path: csiph.com!news.mixmin.net!eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: size_t vs long. Date: Tue, 29 Nov 2022 10:21:13 -0800 Organization: None to speak of Lines: 176 Message-ID: <87tu2hk4fa.fsf@nosuchdomain.example.com> References: <5a71fdad-b7d6-4b4a-a3ee-c5a9beb75d28n@googlegroups.com> <3cd9fa14-f717-40b6-a430-5b6f609fcdc7n@googlegroups.com> <1b4juk47wa.fsf@pfeifferfamily.net> <0a801acb-1c1d-4216-894a-aa11ad307934n@googlegroups.com> MIME-Version: 1.0 Content-Type: text/plain Injection-Info: reader01.eternal-september.org; posting-host="cc7f8f5b661184005b827a5d1852c190"; logging-data="2444771"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18hsZFm8OL8vB8zNMEcdgky" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) Cancel-Lock: sha1:CZN1Iy6RKtAtxsRtc4F3iIpw4mE= sha1:uXADpWXIIcuHrNd9b1QdFlsV4/k= Xref: csiph.com comp.lang.c:168385 A writes: > On Monday, 28 November 2022 at 05:26:26 UTC+5:30, Scott Lurndal wrote: >> "Chris M. Thomasson" writes: >> >On 11/27/2022 3:35 PM, Chris M. Thomasson wrote: >> >> On 11/27/2022 9:36 AM, Joe Pfeiffer wrote: >> >>> "Chris M. Thomasson" writes: >> >>> >> >>>> On 11/25/2022 5:26 PM, Dolores Filandro wrote: >> >>>>> malloc returns NULL if the amount requested exceeds what malloc can >> >>>>> allocate. >> >>>>> There is no wrong behavior or crash from malloc(-1) >> >>>> >> >>>> I remember way back, in 2001'ish where if a malloc failed the server >> >>>> program would go into "panic mode" and start dumping resources. >> >>> >> >>> That's a program that didn't properly handle a null return from >> >>> malloc(), not a problem with malloc(). >> >>> >> >>> Something I do regard as a problem with either malloc(), Linux, or their >> >>> interaction is that malloc() will happily allocate space that doesn't >> >>> exist, and the user won't find out until an attempt is made to access >> >>> the space and a protection violation happens. >> >> >> >> I was testing different methods to handle resources in a server back >> >> then. One of the tests would malloc per-connection state on every new >> >> connection. Sure enough, a stress test would make a malloc return NULL. >> >> So, I would start dumping user state, and try malloc again in an >> >> experiment. Fun times. NT 4.0. Actually, forget about malloc failing for >> >> a moment, I remember some tests where the non-paged memory pool would >> >> get exhausted do to too many pending IOCP actions, and blast the whole >> >> system. >> > >> >For some reason my brain is thinking about an old paper that dealt with >> >so-called cohort scheduling. A method to bunch up like operations in >> >IOCP or the POSIX aio api. Let me try to find the paper... >> > >> Back in the mid 90's, I was working on the Oracle OSD for the Unisys OPUS >> platform (a massively parallel system). The OSD (OS-dependent layer) >> abstracts the hardware from the RDBMS itself. The OPUS systems had >> up to 64 nodes, each with a scsi controller and each with an ethernet >> port (100baseT at that point). We were running OPS (Oracle Parallel >> Server - later called RAC) to exploit the parallelism. >> >> The database was striped across multiple disks on all nodes and OPS >> workloads are dominated by I/O. The core RDBMS passed a list of >> required blocks to the OSD layer and we use the POSIX lio_listio(2) >> function to queue several thousand block requests to the kernel with >> a single system call. The kernel queued all the I/O's to the corresponding >> node automatically and we could poll for completion when prompted by the >> RDBMS. > > > Is size_t usage justified in memcpy()? If a user passes a negative > number by mistake then memcpy() will crash. How do you know it will crash? It's not possible to pass a negative value to memcpy(). It is possible to use a negative value as an argument, but it will be implicitly converted to size_t before being passed. Yes, if you pass -1 as the size argument, it will result in memcpy() receiving SIZE_MAX. This will *probably* result in undefined behavior, because it *probably* exceeds the size of the source and/or target object, and likely the maximum object size the implementation supports. There is no guarantee that it will crash. In the worst case, it will quietly copy more memory than you intended, corrupt memory past the end of the target object, and result in arbitrary misbehavior later on. So don't do that. Seriously, passing a negative value to memcpy() would be a bug, but it's not one that I've ever encountered as far as I know. Programs don't call memcpy() with a size argument obtained from unchecked user input. Have you ever encountered such a bug? You propose changing the way the language defines memcpy() (and a plethora of other functions) to avoid an error that hardly ever occurs. And it's just one of a number of possible errors, such as passing a pointer to an object that no longer exists, computing the size incorrectly in a way that still yields a positive value, trying to copy overlapping objects, passing a pointer to the wrong object, reversing the source and destination pointers, etc. It's not possible to guard against all these errors. It might be possible to build an interface *on top of* memcpy() that guards against many of them. > void *memcpy(void *dest, const void *src, size_t n); > > Shouldn't memcpy() function be like this: > > void *memcpy(void *dest, const void *src, long n) > { > if (n < 0) > return NULL; > > __Rest_of_Code__ > > } No, it shouldn't. The use of size_t by memcpy() is mandated by the ISO C standard. I believe that's been mentioned here a number of times. The standard (nearly) guarantees that the size of any object can be represented by a value of type size_t. It makes no such guarantee for type long. And in fact modern Windows has 32-bit long and 64-bit size_t, and you could have an object whose size requires 33 or more bits to represent it. I believe that's also been mentioned here a number of times. You haven't suggested how that should be addressed. > It can be argued that a large number for 'n' can also crash the system > but we should try to crash as less as possible. At what cost? (And again, a crash is not guaranteed.) > Crashing is good but only during internal testing. Once the system > goes live and people start using it, then if the system crashes then > it is a big problem. And it is quite possible that the system may not > crash in internal testing but may crash when it is live. Yes, bugs happen. I do not suggest radically changing an aspect of the language (abandoning size_t) to prevent one very rare class of bug. > Suppose, a user by mistake enters '-1' for some value asked by the > system and then this value is passed to memcpy(), then memcpy() will > crash. It can be argued that the system can check for negative values, > but it is equivalent to saying that memcpy() can also check for > negative values. That would be the fault of the programmer who passed an unchecked value to memcpy(). > And, in internal testing, it is quite possible than manual testers / > automated tests don't pass a value of '-1' when asked for a value by > the system. They shouldn't be able to. A user typing the string "-1" on standard input should never result in a value of -1 being passed to memcpy(). There needs to be a lot more checking between user input and low-level function calls. > I know glibc won't be changed but in my opinion, where ever size_t is > used in glibc and a '-1' value for it can crash the system, then > size_t is a wrong choice. In my opinion, these functions should > declare size variables as long and check for negative values and > return an error if the value is negative. Why are you still talking about glibc? glibc implements these functions as specifed by the C standard. It cannot change them. Your disgreement is with the way they're defined by the C standard. If I agreed with your underlying point, I would not suggest using long. I would suggest that the type used to represent sizes should be a a signed type rather than an unsigned type. I would suggest standardizing the POSIX signed integer type ssize_t (which can be defined appropriately for each implementation) and using that as the parameter for memcpy(), and deprecating the unsigned type size_t. Again, I do not agree with your underlying point. Accidentally passing a negative value to memcpy() is a rare error, one that I don't think I've ever seen, and that's the entire rationale for your suggestion. But even I agreed, changing memcpy()'s parameter type from size_t to long would be the wrong answer. If you want the kind of memory safety you're looking for, there are other languages that (attempt to) provide it. (Many of them are implemented in C.) -- Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com Working, but not speaking, for XCOM Labs void Void(void) { Void(); } /* The recursive call of the void */