Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: lib/rtl/heap.c * PURPOSE: RTL Heap backend allocator * PROGRAMMERS: Copyright 2010 Aleksey Bragin * Copyright 2020 Katayama Hirofumi MZ */
/* Useful references: https://learn.microsoft.com/en-us/previous-versions/ms810466(v=msdn.10) https://learn.microsoft.com/en-us/previous-versions/ms810603(v=msdn.10) http://www.securitylab.ru/analytics/216376.php http://binglongx.spaces.live.com/blog/cns!142CBF6D49079DE8!596.entry http://www.phreedom.org/research/exploits/asn1-bitstring/ http://illmatics.com/Understanding_the_LFH.pdf http://www.alex-ionescu.com/?p=18*/
/* INCLUDES *****************************************************************/
#include <rtl.h>#include <heap.h>
#define NDEBUG#include <debug.h>
/* Bitmaps stuff */
/* How many least significant bits are clear */UCHAR RtlpBitsClearLow[] ={ 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
FORCEINLINEUCHARRtlpFindLeastSetBit(ULONG Bits){ if (Bits & 0xFFFF) { if (Bits & 0xFF) return RtlpBitsClearLow[Bits & 0xFF]; /* Lowest byte */ else return RtlpBitsClearLow[(Bits >> 8) & 0xFF] + 8; /* 2nd byte */ } else { if ((Bits >> 16) & 0xFF) return RtlpBitsClearLow[(Bits >> 16) & 0xFF] + 16; /* 3rd byte */ else return RtlpBitsClearLow[(Bits >> 24) & 0xFF] + 24; /* Highest byte */ }}
/* Maximum size of a tail-filling pattern used for compare operation */UCHAR FillPattern[HEAP_ENTRY_SIZE] ={ HEAP_TAIL_FILL, HEAP_TAIL_FILL, HEAP_TAIL_FILL, HEAP_TAIL_FILL, HEAP_TAIL_FILL, HEAP_TAIL_FILL, HEAP_TAIL_FILL, HEAP_TAIL_FILL};
staticBOOLEANRtlpIsLastCommittedEntry(PHEAP_ENTRY Entry){ if (Entry->Flags & HEAP_ENTRY_LAST_ENTRY) return TRUE;
Entry = Entry + Entry->Size;
/* 1-sized busy last entry are the committed range guard entries */ if ((Entry->Flags != (HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY)) || (Entry->Size != 1)) return FALSE;
/* This must be the last or the penultimate entry in the page */ ASSERT(((PVOID)PAGE_ROUND_UP(Entry) == (Entry + 1)) || ((PVOID)PAGE_ROUND_UP(Entry)== (Entry + 2))); return TRUE;}
/* FUNCTIONS *****************************************************************/
NTSTATUS NTAPIRtlpInitializeHeap(OUT PHEAP Heap, IN ULONG Flags, IN PHEAP_LOCK Lock OPTIONAL, IN PRTL_HEAP_PARAMETERS Parameters){ ULONG NumUCRs = 8; ULONG Index; SIZE_T HeaderSize; NTSTATUS Status; PHEAP_UCR_DESCRIPTOR UcrDescriptor; SIZE_T DeCommitFreeBlockThreshold;
/* Preconditions */ ASSERT(Heap != NULL); ASSERT(Parameters != NULL); ASSERT(!(Flags & HEAP_LOCK_USER_ALLOCATED)); ASSERT(!(Flags & HEAP_NO_SERIALIZE) || (Lock == NULL)); /* HEAP_NO_SERIALIZE => no lock */
/* Make sure we're not doing stupid things */ DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_ENTRY_SHIFT; /* Start out with the size of a plain Heap header + our hints of free entries + the bitmap */ HeaderSize = FIELD_OFFSET(HEAP, FreeHints[DeCommitFreeBlockThreshold]) + (ROUND_UP(DeCommitFreeBlockThreshold, RTL_BITS_OF(ULONG)) / RTL_BITS_OF(ULONG)) * sizeof(ULONG);
/* Check if space needs to be added for the Heap Lock */ if (!(Flags & HEAP_NO_SERIALIZE)) { if (Lock != NULL) /* The user manages the Heap Lock */ Flags |= HEAP_LOCK_USER_ALLOCATED; else if (RtlpGetMode() == UserMode) { /* In user mode, the Heap Lock trails the Heap header */ Lock = (PHEAP_LOCK) ((ULONG_PTR) (Heap) + HeaderSize); HeaderSize += sizeof(HEAP_LOCK); } }
/* Add space for the initial Heap UnCommitted Range Descriptor list */ UcrDescriptor = (PHEAP_UCR_DESCRIPTOR) ((ULONG_PTR) (Heap) + HeaderSize); HeaderSize += NumUCRs * sizeof(HEAP_UCR_DESCRIPTOR);
HeaderSize = ROUND_UP(HeaderSize, HEAP_ENTRY_SIZE); /* Sanity check */ ASSERT(HeaderSize <= PAGE_SIZE);
/* Initialise the Heap Entry header containing the Heap header */ Heap->Entry.Size = (USHORT)(HeaderSize >> HEAP_ENTRY_SHIFT); Heap->Entry.Flags = HEAP_ENTRY_BUSY; Heap->Entry.SmallTagIndex = LOBYTE(Heap->Entry.Size) ^ HIBYTE(Heap->Entry.Size) ^ Heap->Entry.Flags; Heap->Entry.PreviousSize = 0; Heap->Entry.SegmentOffset = 0; Heap->Entry.UnusedBytes = 0;
/* Initialise the Heap header */ Heap->Signature = HEAP_SIGNATURE; Heap->Flags = Flags; Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE | HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY | HEAP_REALLOC_IN_PLACE_ONLY | HEAP_VALIDATE_PARAMETERS_ENABLED | HEAP_VALIDATE_ALL_ENABLED | HEAP_TAIL_CHECKING_ENABLED | HEAP_CREATE_ALIGN_16 | HEAP_FREE_CHECKING_ENABLED));
/* Initialise the Heap parameters */ Heap->VirtualMemoryThreshold = ROUND_UP(Parameters->VirtualMemoryThreshold, sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT; Heap->SegmentReserve = Parameters->SegmentReserve; Heap->SegmentCommit = Parameters->SegmentCommit; Heap->DeCommitFreeBlockThreshold = DeCommitFreeBlockThreshold; Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_ENTRY_SHIFT; Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize; Heap->CommitRoutine = Parameters->CommitRoutine;
/* Initialise the Heap validation info */ Heap->HeaderValidateCopy = NULL; Heap->HeaderValidateLength = (USHORT)HeaderSize;
/* Initialise the Heap Lock */ if (!(Flags & HEAP_NO_SERIALIZE) && !(Flags & HEAP_LOCK_USER_ALLOCATED)) { Status = RtlInitializeHeapLock(&Lock); if (!NT_SUCCESS(Status)) return Status; } Heap->LockVariable = Lock;
/* Initialise the Heap alignment info */ if (Flags & HEAP_CREATE_ALIGN_16) { Heap->AlignMask = (ULONG) ~15; Heap->AlignRound = 15 + sizeof(HEAP_ENTRY); } else { Heap->AlignMask = (ULONG) ~(sizeof(HEAP_ENTRY) - 1); Heap->AlignRound = 2 * sizeof(HEAP_ENTRY) - 1; }
if (Flags & HEAP_TAIL_CHECKING_ENABLED) Heap->AlignRound += sizeof(HEAP_ENTRY);
/* Initialise the Heap Segment list */ for (Index = 0; Index < HEAP_SEGMENTS; ++Index) Heap->Segments[Index] = NULL;
/* Initialise the free entry lists. */ InitializeListHead(&Heap->FreeLists); RtlInitializeBitMap(&Heap->FreeHintBitmap, (PULONG)&Heap->FreeHints[DeCommitFreeBlockThreshold], DeCommitFreeBlockThreshold); RtlClearAllBits(&Heap->FreeHintBitmap); RtlZeroMemory(&Heap->FreeHints[0], sizeof(Heap->FreeHints[0]) * DeCommitFreeBlockThreshold);
/* Initialise the Heap Virtual Allocated Blocks list */ InitializeListHead(&Heap->VirtualAllocdBlocks);
/* Initialise the Heap UnCommitted Region lists */ InitializeListHead(&Heap->UCRSegments); InitializeListHead(&Heap->UCRList);
/* Register the initial Heap UnCommitted Region Descriptors */ for (Index = 0; Index < NumUCRs; ++Index) InsertTailList(&Heap->UCRList, &UcrDescriptor[Index].ListEntry);
return STATUS_SUCCESS;}
VOID NTAPIRtlpInsertFreeBlockHelper(PHEAP Heap, PHEAP_FREE_ENTRY FreeEntry, SIZE_T BlockSize, BOOLEAN NoFill){ ULONG HintIndex, NextHintIndex;
ASSERT(FreeEntry->Size == BlockSize);
/* Fill if it's not denied */ if (!NoFill) { FreeEntry->Flags &= ~(HEAP_ENTRY_FILL_PATTERN | HEAP_ENTRY_EXTRA_PRESENT | HEAP_ENTRY_BUSY);
if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) { RtlFillMemoryUlong((PCHAR)(FreeEntry + 1), (BlockSize << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry), ARENA_FREE_FILLER);
FreeEntry->Flags |= HEAP_ENTRY_FILL_PATTERN; } } else { /* Clear out all flags except the last entry one */ FreeEntry->Flags &= HEAP_ENTRY_LAST_ENTRY; }
/* See if this should go to the dedicated list */ if (BlockSize > Heap->DeCommitFreeBlockThreshold) { PLIST_ENTRY ListEntry = Heap->FreeHints[0];
/* Check if we have a hint there */ if (ListEntry == NULL) { ASSERT(!RtlTestBit(&Heap->FreeHintBitmap, 0)); Heap->FreeHints[0] = &FreeEntry->FreeList; RtlSetBit(&Heap->FreeHintBitmap, 0); InsertTailList(&Heap->FreeLists, &FreeEntry->FreeList); return; }
ASSERT(RtlTestBit(&Heap->FreeHintBitmap, 0));
while (ListEntry != &Heap->FreeLists) { PHEAP_FREE_ENTRY PreviousEntry = CONTAINING_RECORD(ListEntry, HEAP_FREE_ENTRY, FreeList); if (PreviousEntry->Size >= BlockSize) { DPRINT("Inserting size %lu before %lu.\n", BlockSize, PreviousEntry->Size); break; }
ListEntry = ListEntry->Flink; }
InsertTailList(ListEntry, &FreeEntry->FreeList);
/* Update our hint if needed */ if (Heap->FreeHints[0] == ListEntry) Heap->FreeHints[0] = &FreeEntry->FreeList;
return; }
ASSERT(BlockSize >= 2); HintIndex = BlockSize - 1;
if (Heap->FreeHints[HintIndex] != NULL) { ASSERT(RtlTestBit(&Heap->FreeHintBitmap, HintIndex));
/* Insert it after our hint. */ InsertHeadList(Heap->FreeHints[HintIndex], &FreeEntry->FreeList);
return; }
/* This is the first time we insert such an entry in the list. */ ASSERT(!RtlTestBit(&Heap->FreeHintBitmap, HintIndex)); if (IsListEmpty(&Heap->FreeLists)) { /* First entry inserted in this list ever */ InsertHeadList(&Heap->FreeLists, &FreeEntry->FreeList); RtlSetBit(&Heap->FreeHintBitmap, HintIndex); Heap->FreeHints[HintIndex] = &FreeEntry->FreeList; return; }
/* Find the closest one */ NextHintIndex = RtlFindSetBits(&Heap->FreeHintBitmap, 1, HintIndex); ASSERT(NextHintIndex != 0xFFFFFFFF); if ((NextHintIndex == 0) || (NextHintIndex > HintIndex)) { /* * We found a larger entry. Insert this one before. * It is guaranteed to be our successor in the list. */ InsertTailList(Heap->FreeHints[NextHintIndex], &FreeEntry->FreeList); } else { /* We only found an entry smaller than us. Then we will be the largest one. */ ASSERT(CONTAINING_RECORD(Heap->FreeLists.Blink, HEAP_FREE_ENTRY, FreeList)->Size < BlockSize); InsertTailList(&Heap->FreeLists, &FreeEntry->FreeList); }
/* Setup our hint */ RtlSetBit(&Heap->FreeHintBitmap, HintIndex); Heap->FreeHints[HintIndex] = &FreeEntry->FreeList;}
PHEAP_FREE_ENTRYNTAPIRtlpInsertFreeBlock(PHEAP Heap, PHEAP_FREE_ENTRY FreeEntry, SIZE_T BlockSize){ USHORT Size, PreviousSize; UCHAR SegmentOffset, Flags; PHEAP_SEGMENT Segment; PHEAP_FREE_ENTRY LastEntry;
DPRINT("RtlpInsertFreeBlock(%p %p %x)\n", Heap, FreeEntry, BlockSize);
/* Increase the free size counter */ Heap->TotalFreeSize += BlockSize;
/* Remember certain values */ LastEntry = FreeEntry; Flags = FreeEntry->Flags; PreviousSize = FreeEntry->PreviousSize; SegmentOffset = FreeEntry->SegmentOffset; Segment = Heap->Segments[SegmentOffset];
/* Process it */ while (BlockSize) { /* Check for the max size */ if (BlockSize > HEAP_MAX_BLOCK_SIZE) { Size = HEAP_MAX_BLOCK_SIZE;
/* Special compensation if it goes above limit just by 1 */ if (BlockSize == (HEAP_MAX_BLOCK_SIZE + 1)) Size -= 16;
FreeEntry->Flags = 0; } else { Size = (USHORT)BlockSize; FreeEntry->Flags = Flags; }
/* Change its size and insert it into a free list */ FreeEntry->Size = Size; FreeEntry->PreviousSize = PreviousSize; FreeEntry->SegmentOffset = SegmentOffset;
/* Call a helper to actually insert the block */ RtlpInsertFreeBlockHelper(Heap, FreeEntry, Size, FALSE);
/* Update sizes */ PreviousSize = Size; BlockSize -= Size;
/* Go to the next entry */ LastEntry = FreeEntry; FreeEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + Size);
/* Check if that's all */ if ((PHEAP_ENTRY)FreeEntry >= Segment->LastValidEntry) { return LastEntry; } }
/* Update previous size if needed */ if (!(Flags & HEAP_ENTRY_LAST_ENTRY)) FreeEntry->PreviousSize = PreviousSize;
return LastEntry;}
staticVOIDRtlpRemoveFreeBlock(PHEAP Heap, PHEAP_FREE_ENTRY FreeEntry, BOOLEAN NoFill){ SIZE_T Result, RealSize; ULONG HintIndex;
/* This was a problem before we handled segments > MAXUSHORT. * It may not be needed now, but is left just for safety. */ ASSERT(FreeEntry->Size != 0);
/* Remove the free block */ if (FreeEntry->Size > Heap->DeCommitFreeBlockThreshold) HintIndex = 0; else HintIndex = FreeEntry->Size - 1;
ASSERT(RtlTestBit(&Heap->FreeHintBitmap, HintIndex));
/* Are we removing the hint entry for this size ? */ if (Heap->FreeHints[HintIndex] == &FreeEntry->FreeList) { PHEAP_FREE_ENTRY NewHintEntry = NULL; if (FreeEntry->FreeList.Flink != &Heap->FreeLists) { NewHintEntry = CONTAINING_RECORD(FreeEntry->FreeList.Flink, HEAP_FREE_ENTRY, FreeList); /* * In non-dedicated list, we just put the next entry as hint. * For the dedicated ones, we take care of putting entries of the right size hint. */ if ((HintIndex != 0) && (NewHintEntry->Size != FreeEntry->Size)) { /* Of course this must be a larger one after us */ ASSERT(NewHintEntry->Size > FreeEntry->Size); NewHintEntry = NULL; } }
/* Replace the hint, if we can */ if (NewHintEntry != NULL) { Heap->FreeHints[HintIndex] = &NewHintEntry->FreeList; } else { Heap->FreeHints[HintIndex] = NULL; RtlClearBit(&Heap->FreeHintBitmap, HintIndex); } }
RemoveEntryList(&FreeEntry->FreeList);
/* Fill with pattern if necessary */ if (!NoFill && (FreeEntry->Flags & HEAP_ENTRY_FILL_PATTERN)) { RealSize = (FreeEntry->Size << HEAP_ENTRY_SHIFT) - sizeof(*FreeEntry);
/* Deduct extra stuff from block's real size */ if (FreeEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT && RealSize > sizeof(HEAP_FREE_ENTRY_EXTRA)) { RealSize -= sizeof(HEAP_FREE_ENTRY_EXTRA); }
/* Check if the free filler is intact */ Result = RtlCompareMemoryUlong((PCHAR)(FreeEntry + 1), RealSize, ARENA_FREE_FILLER);
if (Result != RealSize) { DPRINT1("Free heap block %p modified at %p after it was freed\n", FreeEntry, (PCHAR)(FreeEntry + 1) + Result); } }}
SIZE_T NTAPIRtlpGetSizeOfBigBlock(PHEAP_ENTRY HeapEntry){ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
/* Get pointer to the containing record */ VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); ASSERT(VirtualEntry->BusyBlock.Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY));
/* Restore the real size */ return VirtualEntry->CommitSize - HeapEntry->Size;}
PHEAP_UCR_DESCRIPTOR NTAPIRtlpCreateUnCommittedRange(PHEAP_SEGMENT Segment){ PLIST_ENTRY Entry; PHEAP_UCR_DESCRIPTOR UcrDescriptor; PHEAP_UCR_SEGMENT UcrSegment; PHEAP Heap = Segment->Heap; SIZE_T ReserveSize = 16 * PAGE_SIZE; SIZE_T CommitSize = 1 * PAGE_SIZE; NTSTATUS Status;
DPRINT("RtlpCreateUnCommittedRange(%p)\n", Segment);
/* Check if we have unused UCRs */ if (IsListEmpty(&Heap->UCRList)) { /* Get a pointer to the first UCR segment */ UcrSegment = CONTAINING_RECORD(Heap->UCRSegments.Flink, HEAP_UCR_SEGMENT, ListEntry);
/* Check the list of UCR segments */ if (IsListEmpty(&Heap->UCRSegments) || UcrSegment->ReservedSize == UcrSegment->CommittedSize) { /* We need to create a new one. Reserve 16 pages for it */ UcrSegment = NULL; Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID *)&UcrSegment, 0, &ReserveSize, MEM_RESERVE, PAGE_READWRITE);
if (!NT_SUCCESS(Status)) return NULL;
/* Commit one page */ Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID *)&UcrSegment, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE);
if (!NT_SUCCESS(Status)) { /* Release reserved memory */ ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&UcrSegment, &ReserveSize, MEM_RELEASE); return NULL; }
/* Set it's data */ UcrSegment->ReservedSize = ReserveSize; UcrSegment->CommittedSize = CommitSize;
/* Add it to the head of the list */ InsertHeadList(&Heap->UCRSegments, &UcrSegment->ListEntry);
/* Get a pointer to the first available UCR descriptor */ UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)(UcrSegment + 1); } else { /* It's possible to use existing UCR segment. Commit one more page */ UcrDescriptor = (PHEAP_UCR_DESCRIPTOR)((PCHAR)UcrSegment + UcrSegment->CommittedSize); Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID *)&UcrDescriptor, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE);
if (!NT_SUCCESS(Status)) return NULL;
ASSERT((PCHAR)UcrDescriptor == ((PCHAR)UcrSegment + UcrSegment->CommittedSize));
/* Update sizes */ UcrSegment->CommittedSize += CommitSize; }
/* There is a whole bunch of new UCR descriptors. Put them into the unused list */ while ((PCHAR)(UcrDescriptor + 1) <= (PCHAR)UcrSegment + UcrSegment->CommittedSize) { InsertTailList(&Heap->UCRList, &UcrDescriptor->ListEntry); UcrDescriptor++; } }
/* There are unused UCRs, just get the first one */ Entry = RemoveHeadList(&Heap->UCRList); UcrDescriptor = CONTAINING_RECORD(Entry, HEAP_UCR_DESCRIPTOR, ListEntry); return UcrDescriptor;}
VOID NTAPIRtlpDestroyUnCommittedRange(PHEAP_SEGMENT Segment, PHEAP_UCR_DESCRIPTOR UcrDescriptor){ /* Zero it out */ UcrDescriptor->Address = NULL; UcrDescriptor->Size = 0;
/* Put it into the heap's list of unused UCRs */ InsertHeadList(&Segment->Heap->UCRList, &UcrDescriptor->ListEntry);}
VOID NTAPIRtlpInsertUnCommittedPages(PHEAP_SEGMENT Segment, ULONG_PTR Address, SIZE_T Size){ PLIST_ENTRY Current; PHEAP_UCR_DESCRIPTOR UcrDescriptor;
DPRINT("RtlpInsertUnCommittedPages(%p %08Ix %Ix)\n", Segment, Address, Size);
/* Go through the list of UCR descriptors, they are sorted from lowest address to the highest */ Current = Segment->UCRSegmentList.Flink; while (Current != &Segment->UCRSegmentList) { UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
if ((ULONG_PTR)UcrDescriptor->Address > Address) { /* Check for a really lucky case */ if ((Address + Size) == (ULONG_PTR)UcrDescriptor->Address) { /* Exact match */ UcrDescriptor->Address = (PVOID)Address; UcrDescriptor->Size += Size; return; }
/* We found the block before which the new one should go */ break; } else if (((ULONG_PTR)UcrDescriptor->Address + UcrDescriptor->Size) == Address) { /* Modify this entry */ Address = (ULONG_PTR)UcrDescriptor->Address; Size += UcrDescriptor->Size;
/* Advance to the next descriptor */ Current = Current->Flink;
/* Remove the current descriptor from the list and destroy it */ RemoveEntryList(&UcrDescriptor->SegmentEntry); RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
Segment->NumberOfUnCommittedRanges--; } else { /* Advance to the next descriptor */ Current = Current->Flink; } }
/* Create a new UCR descriptor */ UcrDescriptor = RtlpCreateUnCommittedRange(Segment); if (!UcrDescriptor) return;
UcrDescriptor->Address = (PVOID)Address; UcrDescriptor->Size = Size;
/* "Current" is the descriptor before which our one should go */ InsertTailList(Current, &UcrDescriptor->SegmentEntry);
DPRINT("Added segment UCR with base %08Ix, size 0x%x\n", Address, Size);
/* Increase counters */ Segment->NumberOfUnCommittedRanges++;}
staticPHEAP_FREE_ENTRYRtlpFindAndCommitPages(PHEAP Heap, PHEAP_SEGMENT Segment, PSIZE_T Size, PVOID AddressRequested){ PLIST_ENTRY Current; NTSTATUS Status;
DPRINT("RtlpFindAndCommitPages(%p %p %Ix %p)\n", Heap, Segment, *Size, AddressRequested);
/* Go through UCRs in a segment */ Current = Segment->UCRSegmentList.Flink; while (Current != &Segment->UCRSegmentList) { PHEAP_UCR_DESCRIPTOR UcrDescriptor = CONTAINING_RECORD(Current, HEAP_UCR_DESCRIPTOR, SegmentEntry);
/* Check if we can use that one right away */ if (UcrDescriptor->Size >= *Size && (UcrDescriptor->Address == AddressRequested || !AddressRequested)) { PHEAP_ENTRY GuardEntry, FreeEntry; PVOID Address = UcrDescriptor->Address;
/* Commit it */ if (Heap->CommitRoutine) { Status = Heap->CommitRoutine(Heap, &Address, Size); } else { Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &Address, 0, Size, MEM_COMMIT, PAGE_READWRITE); }
DPRINT("Committed %Iu bytes at base %08Ix, UCR size is %lu\n", *Size, Address, UcrDescriptor->Size);
/* Fail in unsuccessful case */ if (!NT_SUCCESS(Status)) { DPRINT1("Committing page failed with status 0x%08X\n", Status); return NULL; }
/* Update tracking numbers */ Segment->NumberOfUnCommittedPages -= (ULONG)(*Size / PAGE_SIZE);
/* Update UCR descriptor */ UcrDescriptor->Address = (PVOID)((ULONG_PTR)UcrDescriptor->Address + *Size); UcrDescriptor->Size -= *Size;
/* Grab the previous guard entry */ GuardEntry = (PHEAP_ENTRY)Address - 1; ASSERT(GuardEntry->Flags & HEAP_ENTRY_LAST_ENTRY); ASSERT(GuardEntry->Flags & HEAP_ENTRY_BUSY); ASSERT(GuardEntry->Size == 1);
/* Did we have a double guard entry ? */ if (GuardEntry->PreviousSize == 1) { /* Use the one before instead */ GuardEntry--;
ASSERT(GuardEntry->Flags & HEAP_ENTRY_LAST_ENTRY); ASSERT(GuardEntry->Flags & HEAP_ENTRY_BUSY); ASSERT(GuardEntry->Size == 1);
/* We gain one slot more */ *Size += HEAP_ENTRY_SIZE; }
/* This will become our returned free entry. * Now we can make it span the whole committed range. * But we keep one slot for a guard entry, if needed. */ FreeEntry = GuardEntry;
FreeEntry->Flags &= ~(HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY); FreeEntry->Size = (*Size) >> HEAP_ENTRY_SHIFT;
DPRINT("Updating UcrDescriptor %p, new Address %p, size %lu\n", UcrDescriptor, UcrDescriptor->Address, UcrDescriptor->Size);
/* Check if anything left in this UCR */ if (UcrDescriptor->Size == 0) { /* It's fully exhausted. Take the guard entry for us */ FreeEntry->Size++; *Size += HEAP_ENTRY_SIZE;
ASSERT((FreeEntry + FreeEntry->Size) == UcrDescriptor->Address);
/* Check if this is the end of the segment */ if(UcrDescriptor->Address == Segment->LastValidEntry) { FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY; } else { PHEAP_ENTRY NextEntry = UcrDescriptor->Address;
/* We should not have a UCR right behind us */ ASSERT((UcrDescriptor->SegmentEntry.Flink == &Segment->UCRSegmentList) || (CONTAINING_RECORD(UcrDescriptor->SegmentEntry.Flink, HEAP_UCR_DESCRIPTOR, SegmentEntry)->Address > UcrDescriptor->Address));
ASSERT(NextEntry->PreviousSize == 0); ASSERT(NextEntry == FreeEntry + FreeEntry->Size); NextEntry->PreviousSize = FreeEntry->Size; }
/* This UCR needs to be removed because it became useless */ RemoveEntryList(&UcrDescriptor->SegmentEntry);
RtlpDestroyUnCommittedRange(Segment, UcrDescriptor); Segment->NumberOfUnCommittedRanges--; } else { /* Setup a guard entry */ GuardEntry = (PHEAP_ENTRY)UcrDescriptor->Address - 1; ASSERT(GuardEntry == FreeEntry + FreeEntry->Size); GuardEntry->Flags = HEAP_ENTRY_LAST_ENTRY | HEAP_ENTRY_BUSY; GuardEntry->Size = 1; GuardEntry->PreviousSize = FreeEntry->Size; GuardEntry->SegmentOffset = FreeEntry->SegmentOffset; DPRINT("Setting %p as UCR guard entry.\n", GuardEntry); }
/* We're done */ return (PHEAP_FREE_ENTRY)FreeEntry; }
/* Advance to the next descriptor */ Current = Current->Flink; }
return NULL;}
staticVOIDRtlpDeCommitFreeBlock(PHEAP Heap, PHEAP_FREE_ENTRY FreeEntry, SIZE_T Size){ PHEAP_SEGMENT Segment; PHEAP_ENTRY NextEntry, GuardEntry; PHEAP_UCR_DESCRIPTOR UcrDescriptor; SIZE_T PrecedingSize, DecommitSize; ULONG_PTR DecommitBase, DecommitEnd; NTSTATUS Status;
DPRINT("Decommitting %p %p %x\n", Heap, FreeEntry, Size);
/* We can't decommit if there is a commit routine! */ if (Heap->CommitRoutine) { /* Just add it back the usual way */ RtlpInsertFreeBlock(Heap, FreeEntry, Size); return; }
/* Get the segment */ Segment = Heap->Segments[FreeEntry->SegmentOffset];
/* Get the preceding entry */ DecommitBase = ROUND_UP(FreeEntry, PAGE_SIZE); PrecedingSize = (PHEAP_ENTRY)DecommitBase - (PHEAP_ENTRY)FreeEntry;
if (PrecedingSize == 0) { /* We need some space in order to insert our guard entry */ DecommitBase += PAGE_SIZE; PrecedingSize += PAGE_SIZE >> HEAP_ENTRY_SHIFT; }
/* Get the entry after this one. */
/* Do we really have a next entry */ if (RtlpIsLastCommittedEntry((PHEAP_ENTRY)FreeEntry)) { /* No, Decommit till the next UCR. */ DecommitEnd = PAGE_ROUND_UP((PHEAP_ENTRY)FreeEntry + FreeEntry->Size); NextEntry = NULL; } else { NextEntry = (PHEAP_ENTRY)FreeEntry + Size; DecommitEnd = PAGE_ROUND_DOWN(NextEntry);
/* Can we make a free entry out of what's left ? */ if ((NextEntry - (PHEAP_ENTRY)DecommitEnd) == 1) { /* Nope. Let's keep one page before this */ DecommitEnd -= PAGE_SIZE; } }
if (DecommitEnd <= DecommitBase) { /* There's nothing left to decommit. */ RtlpInsertFreeBlock(Heap, FreeEntry, Size); return; }
DecommitSize = DecommitEnd - DecommitBase;
/* A decommit is necessary. Create a UCR descriptor */ UcrDescriptor = RtlpCreateUnCommittedRange(Segment); if (!UcrDescriptor) { DPRINT1("HEAP: Failed to create UCR descriptor\n"); RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize); return; }
/* Decommit the memory */ Status = ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&DecommitBase, &DecommitSize, MEM_DECOMMIT); ASSERT((DecommitBase + DecommitSize) == DecommitEnd);
/* Delete that UCR. This is needed to assure there is an unused UCR entry in the list */ RtlpDestroyUnCommittedRange(Segment, UcrDescriptor);
if (!NT_SUCCESS(Status)) { RtlpInsertFreeBlock(Heap, FreeEntry, Size); return; }
/* Insert uncommitted pages */ RtlpInsertUnCommittedPages(Segment, DecommitBase, DecommitSize); Segment->NumberOfUnCommittedPages += (ULONG)(DecommitSize / PAGE_SIZE);
/* Insert our guard entry before this */ GuardEntry = (PHEAP_ENTRY)DecommitBase - 1; GuardEntry->Size = 1; GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; GuardEntry->SegmentOffset = FreeEntry->SegmentOffset; DPRINT("Setting %p as UCR guard entry.\n", GuardEntry);
/* Now see what's really behind us */ PrecedingSize--; switch (PrecedingSize) { case 1: /* No space left for a free entry. Make this another guard entry */ GuardEntry->PreviousSize = 1; GuardEntry--; GuardEntry->Size = 1; GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; GuardEntry->SegmentOffset = FreeEntry->SegmentOffset; /* Fall-through */ case 0: /* There was just enough space four our guard entry */ ASSERT((PHEAP_ENTRY)FreeEntry == GuardEntry); GuardEntry->PreviousSize = FreeEntry->PreviousSize; break; default: /* We can insert this as a free entry */ GuardEntry->PreviousSize = PrecedingSize; FreeEntry->Size = PrecedingSize; FreeEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY; FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &PrecedingSize, FALSE); RtlpInsertFreeBlock(Heap, FreeEntry, PrecedingSize); break; }
/* Now the next one */ if (NextEntry) { ASSERT((PHEAP_ENTRY)DecommitEnd <= NextEntry);
SIZE_T NextSize = NextEntry - (PHEAP_ENTRY)DecommitEnd; if (NextSize) { PHEAP_FREE_ENTRY NextFreeEntry = (PHEAP_FREE_ENTRY)DecommitEnd;
/* Make sure this is all valid */ ASSERT((PHEAP_ENTRY)DecommitEnd < Segment->LastValidEntry); ASSERT(NextSize >= 2);
/* Adjust size of this free entry and insert it */ NextFreeEntry->Flags = 0; NextFreeEntry->PreviousSize = 0; NextFreeEntry->SegmentOffset = Segment->Entry.SegmentOffset; NextFreeEntry->Size = (USHORT)NextSize;
NextEntry->PreviousSize = NextSize; ASSERT(NextEntry == (PHEAP_ENTRY)NextFreeEntry + NextFreeEntry->Size);
NextFreeEntry = RtlpCoalesceFreeBlocks(Heap, NextFreeEntry, &NextSize, FALSE); RtlpInsertFreeBlock(Heap, NextFreeEntry, NextSize); } else { /* This one must be at the beginning of a page */ ASSERT(NextEntry == (PHEAP_ENTRY)PAGE_ROUND_DOWN(NextEntry)); /* And we must have a gap betwwen */ ASSERT(NextEntry > (PHEAP_ENTRY)DecommitBase); NextEntry->PreviousSize = 0; } }}
NTSTATUSNTAPIRtlpInitializeHeapSegment(IN OUT PHEAP Heap, OUT PHEAP_SEGMENT Segment, IN UCHAR SegmentIndex, IN ULONG SegmentFlags, IN SIZE_T SegmentReserve, IN SIZE_T SegmentCommit){ /* Preconditions */ ASSERT(Heap != NULL); ASSERT(Segment != NULL); ASSERT(SegmentCommit >= PAGE_SIZE); ASSERT(ROUND_DOWN(SegmentCommit, PAGE_SIZE) == SegmentCommit); ASSERT(SegmentReserve >= SegmentCommit); ASSERT(ROUND_DOWN(SegmentReserve, PAGE_SIZE) == SegmentReserve);
DPRINT("RtlpInitializeHeapSegment(%p %p %x %x %lx %lx)\n", Heap, Segment, SegmentIndex, SegmentFlags, SegmentReserve, SegmentCommit);
/* Initialise the Heap Entry header if this is not the first Heap Segment */ if ((PHEAP_SEGMENT) (Heap) != Segment) { Segment->Entry.Size = ROUND_UP(sizeof(HEAP_SEGMENT), sizeof(HEAP_ENTRY)) >> HEAP_ENTRY_SHIFT; Segment->Entry.Flags = HEAP_ENTRY_BUSY; Segment->Entry.SmallTagIndex = LOBYTE(Segment->Entry.Size) ^ HIBYTE(Segment->Entry.Size) ^ Segment->Entry.Flags; Segment->Entry.PreviousSize = 0; Segment->Entry.SegmentOffset = SegmentIndex; Segment->Entry.UnusedBytes = 0; }
/* Sanity check */ ASSERT((Segment->Entry.Size << HEAP_ENTRY_SHIFT) <= PAGE_SIZE);
/* Initialise the Heap Segment header */ Segment->SegmentSignature = HEAP_SEGMENT_SIGNATURE; Segment->SegmentFlags = SegmentFlags; Segment->Heap = Heap; Heap->Segments[SegmentIndex] = Segment;
/* Initialise the Heap Segment location information */ Segment->BaseAddress = Segment; Segment->NumberOfPages = (ULONG)(SegmentReserve >> PAGE_SHIFT);
/* Initialise the Heap Entries contained within the Heap Segment */ Segment->FirstEntry = &Segment->Entry + Segment->Entry.Size; Segment->LastValidEntry = (PHEAP_ENTRY)((ULONG_PTR)Segment + SegmentReserve);
/* Initialise the Heap Segment UnCommitted Range information */ Segment->NumberOfUnCommittedPages = (ULONG)((SegmentReserve - SegmentCommit) >> PAGE_SHIFT); Segment->NumberOfUnCommittedRanges = 0; InitializeListHead(&Segment->UCRSegmentList);
/* We must have space for a guard entry ! */ ASSERT (((SegmentCommit >> HEAP_ENTRY_SHIFT) > Segment->Entry.Size) || (Segment->NumberOfUnCommittedPages == 0));
if (((SIZE_T)Segment->Entry.Size << HEAP_ENTRY_SHIFT) < SegmentCommit) { PHEAP_ENTRY FreeEntry = NULL;
if (Segment->NumberOfUnCommittedPages != 0) { /* Ensure we put our guard entry at the end of the last committed page */ PHEAP_ENTRY GuardEntry = &Segment->Entry + (SegmentCommit >> HEAP_ENTRY_SHIFT) - 1; SIZE_T PreviousSize;
ASSERT(GuardEntry > &Segment->Entry); GuardEntry->Size = 1; GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; GuardEntry->SegmentOffset = SegmentIndex; PreviousSize = GuardEntry - Segment->FirstEntry;
/* Check what is left behind us */ switch (PreviousSize) { case 1: GuardEntry->PreviousSize = PreviousSize;
/* There is not enough space for a free entry. Double the guard entry */ GuardEntry--; GuardEntry->Size = 1; GuardEntry->Flags = HEAP_ENTRY_BUSY | HEAP_ENTRY_LAST_ENTRY; GuardEntry->SegmentOffset = SegmentIndex; DPRINT1("Setting %p as UCR guard entry.\n", GuardEntry); /* Fall through */ case 0: ASSERT(GuardEntry == Segment->FirstEntry); GuardEntry->PreviousSize = Segment->Entry.Size; break; default: /* There will be a free entry between the segment and the guard entry */ FreeEntry = Segment->FirstEntry; FreeEntry->PreviousSize = Segment->Entry.Size; FreeEntry->SegmentOffset = SegmentIndex; FreeEntry->Size = PreviousSize; FreeEntry->Flags = 0;
/* Register the Free Heap Entry */ FreeEntry = (PHEAP_ENTRY)RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)FreeEntry, PreviousSize); GuardEntry->PreviousSize = FreeEntry->Size; break; } } else { /* Prepare a Free Heap Entry header */ FreeEntry = Segment->FirstEntry; FreeEntry->PreviousSize = Segment->Entry.Size; FreeEntry->SegmentOffset = SegmentIndex; FreeEntry->Flags = HEAP_ENTRY_LAST_ENTRY; FreeEntry->Size = (SegmentCommit >> HEAP_ENTRY_SHIFT) - Segment->Entry.Size;
/* Register the Free Heap Entry */ RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)FreeEntry, FreeEntry->Size); } }
/* Register the UnCommitted Range of the Heap Segment */ if (Segment->NumberOfUnCommittedPages != 0) RtlpInsertUnCommittedPages(Segment, (ULONG_PTR) (Segment) + SegmentCommit, SegmentReserve - SegmentCommit);
return STATUS_SUCCESS;}
VOID NTAPIRtlpDestroyHeapSegment(PHEAP_SEGMENT Segment){ NTSTATUS Status; PVOID BaseAddress; SIZE_T Size = 0;
/* Make sure it's not user allocated */ if (Segment->SegmentFlags & HEAP_USER_ALLOCATED) return;
BaseAddress = Segment->BaseAddress; DPRINT("Destroying segment %p, BA %p\n", Segment, BaseAddress);
/* Release virtual memory */ Status = ZwFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &Size, MEM_RELEASE);
if (!NT_SUCCESS(Status)) { DPRINT1("HEAP: Failed to release segment's memory with status 0x%08X\n", Status); }}
PHEAP_FREE_ENTRY NTAPIRtlpCoalesceHeap(PHEAP Heap){ UNIMPLEMENTED; return NULL;}
PHEAP_FREE_ENTRY NTAPIRtlpCoalesceFreeBlocks (PHEAP Heap, PHEAP_FREE_ENTRY FreeEntry, PSIZE_T FreeSize, BOOLEAN Remove){ PHEAP_FREE_ENTRY CurrentEntry, NextEntry; UCHAR SegmentOffset;
/* Get the previous entry */ CurrentEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry - FreeEntry->PreviousSize);
/* Check it */ if (CurrentEntry != FreeEntry && !(CurrentEntry->Flags & HEAP_ENTRY_BUSY) && (*FreeSize + CurrentEntry->Size) <= HEAP_MAX_BLOCK_SIZE) { ASSERT(FreeEntry->PreviousSize == CurrentEntry->Size);
/* Remove it if asked for */ if (Remove) { RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); Heap->TotalFreeSize -= FreeEntry->Size;
/* Remove it only once! */ Remove = FALSE; }
/* Remove previous entry too */ RtlpRemoveFreeBlock(Heap, CurrentEntry, FALSE);
/* Copy flags */ CurrentEntry->Flags = FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
/* Advance FreeEntry and update sizes */ FreeEntry = CurrentEntry; *FreeSize = *FreeSize + CurrentEntry->Size; Heap->TotalFreeSize -= CurrentEntry->Size; FreeEntry->Size = (USHORT)(*FreeSize);
/* Also update previous size if needed */ if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) { ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize); } else { SegmentOffset = FreeEntry->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS); } }
/* Check the next block if it exists */ if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) { NextEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeEntry + *FreeSize);
if (!(NextEntry->Flags & HEAP_ENTRY_BUSY) && NextEntry->Size + *FreeSize <= HEAP_MAX_BLOCK_SIZE) { ASSERT(*FreeSize == NextEntry->PreviousSize);
/* Remove it if asked for */ if (Remove) { RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); Heap->TotalFreeSize -= FreeEntry->Size; }
/* Copy flags */ FreeEntry->Flags = NextEntry->Flags & HEAP_ENTRY_LAST_ENTRY;
/* Remove next entry now */ RtlpRemoveFreeBlock(Heap, NextEntry, FALSE);
/* Update sizes */ *FreeSize = *FreeSize + NextEntry->Size; Heap->TotalFreeSize -= NextEntry->Size; FreeEntry->Size = (USHORT)(*FreeSize);
/* Also update previous size if needed */ if (!(FreeEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) { ((PHEAP_ENTRY)FreeEntry + *FreeSize)->PreviousSize = (USHORT)(*FreeSize); } else { SegmentOffset = FreeEntry->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS); } } } return FreeEntry;}
staticPHEAP_FREE_ENTRYRtlpExtendHeap(PHEAP Heap, SIZE_T Size){ ULONG Pages; UCHAR Index, EmptyIndex; SIZE_T FreeSize, CommitSize, ReserveSize; PHEAP_SEGMENT Segment; PHEAP_FREE_ENTRY FreeEntry; NTSTATUS Status;
DPRINT("RtlpExtendHeap(%p %x)\n", Heap, Size);
/* Calculate amount in pages */ Pages = (ULONG)((Size + PAGE_SIZE - 1) / PAGE_SIZE); FreeSize = Pages * PAGE_SIZE; DPRINT("Pages %x, FreeSize %x. Going through segments...\n", Pages, FreeSize);
/* Find an empty segment */ EmptyIndex = HEAP_SEGMENTS; for (Index = 0; Index < HEAP_SEGMENTS; Index++) { Segment = Heap->Segments[Index];
if (Segment) DPRINT("Segment[%u] %p with NOUCP %x\n", Index, Segment, Segment->NumberOfUnCommittedPages);
/* Check if its size suits us */ if (Segment && Pages <= Segment->NumberOfUnCommittedPages) { DPRINT("This segment is suitable\n");
/* Commit needed amount */ FreeEntry = RtlpFindAndCommitPages(Heap, Segment, &FreeSize, NULL);
/* Coalesce it with adjacent entries */ if (FreeEntry) { FreeSize = FreeSize >> HEAP_ENTRY_SHIFT; FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE); RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize); return FreeEntry; } } else if (!Segment && EmptyIndex == HEAP_SEGMENTS) { /* Remember the first unused segment index */ EmptyIndex = Index; } }
/* No luck, need to grow the heap */ if ((Heap->Flags & HEAP_GROWABLE) && (EmptyIndex != HEAP_SEGMENTS)) { Segment = NULL;
/* Reserve the memory */ if ((Size + PAGE_SIZE) <= Heap->SegmentReserve) ReserveSize = Heap->SegmentReserve; else ReserveSize = Size + PAGE_SIZE;
Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID)&Segment, 0, &ReserveSize, MEM_RESERVE, PAGE_READWRITE);
/* If it failed, retry again with a half division algorithm */ while (!NT_SUCCESS(Status) && ReserveSize != Size + PAGE_SIZE) { ReserveSize /= 2;
if (ReserveSize < (Size + PAGE_SIZE)) ReserveSize = Size + PAGE_SIZE;
Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID)&Segment, 0, &ReserveSize, MEM_RESERVE, PAGE_READWRITE); }
/* Proceed only if it's success */ if (NT_SUCCESS(Status)) { Heap->SegmentReserve += ReserveSize;
/* Now commit the memory */ if ((Size + PAGE_SIZE) <= Heap->SegmentCommit) CommitSize = Heap->SegmentCommit; else CommitSize = Size + PAGE_SIZE;
Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID)&Segment, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE);
DPRINT("Committed %lu bytes at base %p\n", CommitSize, Segment);
/* Initialize heap segment if commit was successful */ if (NT_SUCCESS(Status)) Status = RtlpInitializeHeapSegment(Heap, Segment, EmptyIndex, 0, ReserveSize, CommitSize);
/* If everything worked - cool */ if (NT_SUCCESS(Status)) return (PHEAP_FREE_ENTRY)Segment->FirstEntry;
DPRINT1("Committing failed with status 0x%08X\n", Status);
/* Nope, we failed. Free memory */ ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID)&Segment, &ReserveSize, MEM_RELEASE); } else { DPRINT1("Reserving failed with status 0x%08X\n", Status); } }
if (RtlpGetMode() == UserMode) { /* If coalescing on free is disabled in usermode, then do it here */ if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE) { FreeEntry = RtlpCoalesceHeap(Heap);
/* If it's a suitable one - return it */ if (FreeEntry && FreeEntry->Size >= Size) { return FreeEntry; } } }
return NULL;}
/*********************************************************************** * RtlCreateHeap * RETURNS * Handle of heap: Success * NULL: Failure * * @implemented */HANDLE NTAPIRtlCreateHeap(ULONG Flags, PVOID Addr, SIZE_T TotalSize, SIZE_T CommitSize, PVOID Lock, PRTL_HEAP_PARAMETERS Parameters){ PVOID CommittedAddress = NULL, UncommittedAddress = NULL; PHEAP Heap = NULL; RTL_HEAP_PARAMETERS SafeParams = {0}; ULONG_PTR MaximumUserModeAddress; SYSTEM_BASIC_INFORMATION SystemInformation; MEMORY_BASIC_INFORMATION MemoryInfo; ULONG NtGlobalFlags = RtlGetNtGlobalFlags(); ULONG HeapSegmentFlags = 0; NTSTATUS Status; ULONG MaxBlockSize;
/* Check for a special heap */ if (RtlpPageHeapEnabled && !Addr && !Lock) { Heap = RtlpPageHeapCreate(Flags, Addr, TotalSize, CommitSize, Lock, Parameters); if (Heap) return Heap;
/* Reset a special Parameters == -1 hack */ if ((ULONG_PTR)Parameters == (ULONG_PTR)-1) Parameters = NULL; else DPRINT1("Enabling page heap failed\n"); }
/* Check validation flags */ if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS) && (Flags & ~HEAP_CREATE_VALID_MASK)) { DPRINT1("Invalid flags 0x%08x, fixing...\n", Flags); Flags &= HEAP_CREATE_VALID_MASK; }
/* Capture parameters */ if (Parameters) { _SEH2_TRY { /* If size of structure correct, then copy it */ if (Parameters->Length == sizeof(RTL_HEAP_PARAMETERS)) RtlCopyMemory(&SafeParams, Parameters, sizeof(RTL_HEAP_PARAMETERS)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return NULL); } _SEH2_END; }
Parameters = &SafeParams;
/* Check global flags */ if (NtGlobalFlags & FLG_HEAP_DISABLE_COALESCING) Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
if (NtGlobalFlags & FLG_HEAP_ENABLE_FREE_CHECK) Flags |= HEAP_FREE_CHECKING_ENABLED;
if (NtGlobalFlags & FLG_HEAP_ENABLE_TAIL_CHECK) Flags |= HEAP_TAIL_CHECKING_ENABLED;
if (RtlpGetMode() == UserMode) { /* Also check these flags if in usermode */ if (NtGlobalFlags & FLG_HEAP_VALIDATE_ALL) Flags |= HEAP_VALIDATE_ALL_ENABLED;
if (NtGlobalFlags & FLG_HEAP_VALIDATE_PARAMETERS) Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
if (NtGlobalFlags & FLG_USER_STACK_TRACE_DB) Flags |= HEAP_CAPTURE_STACK_BACKTRACES; }
/* Set tunable parameters */ RtlpSetHeapParameters(Parameters);
/* Get the max um address */ Status = ZwQuerySystemInformation(SystemBasicInformation, &SystemInformation, sizeof(SystemInformation), NULL);
if (!NT_SUCCESS(Status)) { DPRINT1("Getting max usermode address failed with status 0x%08x\n", Status); return NULL; }
MaximumUserModeAddress = SystemInformation.MaximumUserModeAddress;
/* Calculate max alloc size */ if (!Parameters->MaximumAllocationSize) Parameters->MaximumAllocationSize = MaximumUserModeAddress - (ULONG_PTR)0x10000 - PAGE_SIZE;
MaxBlockSize = 0x80000 - PAGE_SIZE;
if (!Parameters->VirtualMemoryThreshold || Parameters->VirtualMemoryThreshold > MaxBlockSize) { Parameters->VirtualMemoryThreshold = MaxBlockSize; }
if (Parameters->DeCommitFreeBlockThreshold != PAGE_SIZE) { DPRINT1("WARNING: Ignoring DeCommitFreeBlockThreshold %lx, setting it to PAGE_SIZE.\n", Parameters->DeCommitFreeBlockThreshold); Parameters->DeCommitFreeBlockThreshold = PAGE_SIZE; }
/* Check reserve/commit sizes and set default values */ if (!CommitSize) { CommitSize = PAGE_SIZE; if (TotalSize) TotalSize = ROUND_UP(TotalSize, PAGE_SIZE); else TotalSize = 64 * PAGE_SIZE; } else { /* Round up the commit size to be at least the page size */ CommitSize = ROUND_UP(CommitSize, PAGE_SIZE);
if (TotalSize) TotalSize = ROUND_UP(TotalSize, PAGE_SIZE); else TotalSize = ROUND_UP(CommitSize, 16 * PAGE_SIZE); }
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugCreateHeap(Flags, Addr, TotalSize, CommitSize, Lock, Parameters);
/* Without serialization, a lock makes no sense */ if ((Flags & HEAP_NO_SERIALIZE) && (Lock != NULL)) return NULL;
/* See if we are already provided with an address for the heap */ if (Addr) { if (Parameters->CommitRoutine) { /* There is a commit routine, so no problem here, check params */ if ((Flags & HEAP_GROWABLE) || !Parameters->InitialCommit || !Parameters->InitialReserve || (Parameters->InitialCommit > Parameters->InitialReserve)) { /* Fail */ return NULL; }
/* Calculate committed and uncommitted addresses */ CommittedAddress = Addr; UncommittedAddress = (PCHAR)Addr + Parameters->InitialCommit; TotalSize = Parameters->InitialReserve;
/* Zero the initial page ourselves */ RtlZeroMemory(CommittedAddress, PAGE_SIZE); } else { /* Commit routine is absent, so query how much memory caller reserved */ Status = ZwQueryVirtualMemory(NtCurrentProcess(), Addr, MemoryBasicInformation, &MemoryInfo, sizeof(MemoryInfo), NULL);
if (!NT_SUCCESS(Status)) { DPRINT1("Querying amount of user supplied memory failed with status 0x%08X\n", Status); return NULL; }
/* Validate it */ if (MemoryInfo.BaseAddress != Addr || MemoryInfo.State == MEM_FREE) { return NULL; }
/* Validation checks passed, set committed/uncommitted addresses */ CommittedAddress = Addr;
/* Check if it's committed or not */ if (MemoryInfo.State == MEM_COMMIT) { /* Zero it out because it's already committed */ RtlZeroMemory(CommittedAddress, PAGE_SIZE);
/* Calculate uncommitted address value */ CommitSize = MemoryInfo.RegionSize; TotalSize = CommitSize; UncommittedAddress = (PCHAR)Addr + CommitSize;
/* Check if uncommitted address is reserved */ Status = ZwQueryVirtualMemory(NtCurrentProcess(), UncommittedAddress, MemoryBasicInformation, &MemoryInfo, sizeof(MemoryInfo), NULL);
if (NT_SUCCESS(Status) && MemoryInfo.State == MEM_RESERVE) { /* It is, so add it up to the reserve size */ TotalSize += MemoryInfo.RegionSize; } } else { /* It's not committed, inform following code that a commit is necessary */ CommitSize = PAGE_SIZE; UncommittedAddress = Addr; } }
/* Mark this as a user-committed mem */ HeapSegmentFlags = HEAP_USER_ALLOCATED; Heap = (PHEAP)Addr; } else { /* Check commit routine */ if (Parameters->CommitRoutine) return NULL;
/* Reserve memory */ Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID *)&Heap, 0, &TotalSize, MEM_RESERVE, PAGE_READWRITE);
if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reserve memory with status 0x%08x\n", Status); return NULL; }
/* Set base addresses */ CommittedAddress = Heap; UncommittedAddress = Heap; }
/* Check if we need to commit something */ if (CommittedAddress == UncommittedAddress) { /* Commit the required size */ Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &CommittedAddress, 0, &CommitSize, MEM_COMMIT, PAGE_READWRITE);
DPRINT("Committed %Iu bytes at base %p\n", CommitSize, CommittedAddress);
if (!NT_SUCCESS(Status)) { DPRINT1("Failure, Status 0x%08X\n", Status);
/* Release memory if it was reserved */ if (!Addr) ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&Heap, &TotalSize, MEM_RELEASE);
return NULL; }
/* Calculate new uncommitted address */ UncommittedAddress = (PCHAR)UncommittedAddress + CommitSize; }
/* Initialize the heap */ Status = RtlpInitializeHeap(Heap, Flags, Lock, Parameters); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to initialize heap (%x)\n", Status); return NULL; }
/* Initialize heap's first segment */ Status = RtlpInitializeHeapSegment(Heap, (PHEAP_SEGMENT) (Heap), 0, HeapSegmentFlags, TotalSize, CommitSize); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to initialize heap segment (%x)\n", Status); return NULL; }
DPRINT("Created heap %p, CommitSize %x, ReserveSize %x\n", Heap, CommitSize, TotalSize);
/* Add heap to process list in case of usermode heap */ if (RtlpGetMode() == UserMode) { RtlpAddHeapToProcessList(Heap);
// FIXME: What about lookasides? }
return Heap;}
/*********************************************************************** * RtlDestroyHeap * RETURNS * TRUE: Success * FALSE: Failure * * @implemented * * RETURNS * Success: A NULL HANDLE, if heap is NULL or it was destroyed * Failure: The Heap handle, if heap is the process heap. */HANDLE NTAPIRtlDestroyHeap(HANDLE HeapPtr) /* [in] Handle of heap */{ PHEAP Heap = (PHEAP)HeapPtr; PLIST_ENTRY Current; PHEAP_UCR_SEGMENT UcrSegment; PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; PVOID BaseAddress; SIZE_T Size; LONG i; PHEAP_SEGMENT Segment;
if (!HeapPtr) return NULL;
/* Call page heap routine if required */ if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) return RtlpPageHeapDestroy(HeapPtr);
/* Call special heap */ if (RtlpHeapIsSpecial(Heap->Flags)) { if (!RtlDebugDestroyHeap(Heap)) return HeapPtr; }
/* Check for a process heap */ if (RtlpGetMode() == UserMode && HeapPtr == NtCurrentPeb()->ProcessHeap) return HeapPtr;
/* Free up all big allocations */ Current = Heap->VirtualAllocdBlocks.Flink; while (Current != &Heap->VirtualAllocdBlocks) { VirtualEntry = CONTAINING_RECORD(Current, HEAP_VIRTUAL_ALLOC_ENTRY, Entry); BaseAddress = (PVOID)VirtualEntry; Current = Current->Flink; Size = 0; ZwFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &Size, MEM_RELEASE); }
/* Delete tags and remove heap from the process heaps list in user mode */ if (RtlpGetMode() == UserMode) { // FIXME DestroyTags RtlpRemoveHeapFromProcessList(Heap); }
/* Delete the heap lock */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { /* Delete it if it wasn't user allocated */ if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED)) RtlDeleteHeapLock(Heap->LockVariable);
/* Clear out the lock variable */ Heap->LockVariable = NULL; }
/* Free UCR segments if any were created */ Current = Heap->UCRSegments.Flink; while (Current != &Heap->UCRSegments) { UcrSegment = CONTAINING_RECORD(Current, HEAP_UCR_SEGMENT, ListEntry);
/* Advance to the next descriptor */ Current = Current->Flink;
BaseAddress = (PVOID)UcrSegment; Size = 0;
/* Release that memory */ ZwFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &Size, MEM_RELEASE); }
/* Go through segments and destroy them */ for (i = HEAP_SEGMENTS - 1; i >= 0; i--) { Segment = Heap->Segments[i]; if (Segment) RtlpDestroyHeapSegment(Segment); }
return NULL;}
PHEAP_ENTRY NTAPIRtlpSplitEntry(PHEAP Heap, ULONG Flags, PHEAP_FREE_ENTRY FreeBlock, SIZE_T AllocationSize, SIZE_T Index, SIZE_T Size){ PHEAP_FREE_ENTRY SplitBlock, SplitBlock2; UCHAR FreeFlags, EntryFlags = HEAP_ENTRY_BUSY; PHEAP_ENTRY InUseEntry; SIZE_T FreeSize; UCHAR SegmentOffset;
/* Add extra flags in case of settable user value feature is requested, or there is a tag (small or normal) or there is a request to capture stack backtraces */ if ((Flags & HEAP_EXTRA_FLAGS_MASK) || Heap->PseudoTagEntries) { /* Add flag which means that the entry will have extra stuff attached */ EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
/* NB! AllocationSize is already adjusted by RtlAllocateHeap */ }
/* Add settable user flags, if any */ EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
/* Save flags, update total free size */ FreeFlags = FreeBlock->Flags; Heap->TotalFreeSize -= FreeBlock->Size;
/* Make this block an in-use one */ InUseEntry = (PHEAP_ENTRY)FreeBlock; InUseEntry->Flags = EntryFlags; InUseEntry->SmallTagIndex = 0;
/* Calculate the extra amount */ FreeSize = InUseEntry->Size - Index;
/* Update it's size fields (we don't need their data anymore) */ InUseEntry->Size = (USHORT)Index; InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size);
/* If there is something to split - do the split */ if (FreeSize != 0) { /* Don't split if resulting entry can't contain any payload data (i.e. being just HEAP_ENTRY_SIZE) */ if (FreeSize == 1) { /* Increase sizes of the in-use entry */ InUseEntry->Size++; InUseEntry->UnusedBytes += sizeof(HEAP_ENTRY); } else { /* Calculate a pointer to the new entry */ SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
/* Initialize it */ SplitBlock->Flags = FreeFlags; SplitBlock->SegmentOffset = InUseEntry->SegmentOffset; SplitBlock->Size = (USHORT)FreeSize; SplitBlock->PreviousSize = (USHORT)Index;
/* Check if it's the last entry */ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) { /* Insert it to the free list if it's the last entry */ RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); Heap->TotalFreeSize += FreeSize; } else { /* Not so easy - need to update next's previous size too */ SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) { SplitBlock2->PreviousSize = (USHORT)FreeSize; RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); Heap->TotalFreeSize += FreeSize; } else { /* Even more complex - the next entry is free, so we can merge them into one! */ SplitBlock->Flags = SplitBlock2->Flags;
/* Remove that next entry */ RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE);
/* Update sizes */ FreeSize += SplitBlock2->Size; Heap->TotalFreeSize -= SplitBlock2->Size;
if (FreeSize <= HEAP_MAX_BLOCK_SIZE) { /* Insert it back */ SplitBlock->Size = (USHORT)FreeSize;
/* Don't forget to update previous size of the next entry! */ if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) { ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize; }
/* Actually insert it */ RtlpInsertFreeBlockHelper(Heap, SplitBlock, (USHORT)FreeSize, FALSE);
/* Update total size */ Heap->TotalFreeSize += FreeSize; } else { /* Resulting block is quite big */ RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize); } } }
/* Reset flags of the free entry */ FreeFlags = 0; if (SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY) { SegmentOffset = SplitBlock->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS); } } }
/* Set last entry flag */ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) InUseEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
return InUseEntry;}
staticPVOIDRtlpAllocateNonDedicated(PHEAP Heap, ULONG Flags, SIZE_T Size, SIZE_T AllocationSize, SIZE_T Index, BOOLEAN HeapLocked){ PHEAP_FREE_ENTRY FreeBlock;
/* The entries in the list must be too small for us */ ASSERT(IsListEmpty(&Heap->FreeLists) || (CONTAINING_RECORD(Heap->FreeLists.Blink, HEAP_FREE_ENTRY, FreeList)->Size < Index));
/* Extend the heap */ FreeBlock = RtlpExtendHeap(Heap, AllocationSize);
/* Use the new biggest entry we've got */ if (FreeBlock) { PHEAP_ENTRY InUseEntry; PHEAP_ENTRY_EXTRA Extra;
RtlpRemoveFreeBlock(Heap, FreeBlock, TRUE);
/* Split it */ InUseEntry = RtlpSplitEntry(Heap, Flags, FreeBlock, AllocationSize, Index, Size);
/* Release the lock */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
/* Zero memory if that was requested */ if (Flags & HEAP_ZERO_MEMORY) RtlZeroMemory(InUseEntry + 1, Size); else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) { /* Fill this block with a special pattern */ RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER); }
/* Fill tail of the block with a special pattern too if requested */ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) { RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL); InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN; }
/* Prepare extra if it's present */ if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { Extra = RtlpGetExtraStuffPointer(InUseEntry); RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
// TODO: Tagging }
/* Return pointer to the */ return InUseEntry + 1; }
/* Really unfortunate, out of memory condition */ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
/* Generate an exception */ if (Flags & HEAP_GENERATE_EXCEPTIONS) { EXCEPTION_RECORD ExceptionRecord;
ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionInformation[0] = AllocationSize;
RtlRaiseException(&ExceptionRecord); }
/* Release the lock */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); DPRINT1("HEAP: Allocation failed!\n"); DPRINT1("Flags %x\n", Heap->Flags); return NULL;}
/*********************************************************************** * HeapAlloc (KERNEL32.334) * RETURNS * Pointer to allocated memory block * NULL: Failure * 0x7d030f60--invalid flags in RtlHeapAllocate * @implemented */PVOID NTAPIRtlAllocateHeap(IN PVOID HeapPtr, IN ULONG Flags, IN SIZE_T Size){ PHEAP Heap = (PHEAP)HeapPtr; SIZE_T AllocationSize; SIZE_T Index; UCHAR EntryFlags = HEAP_ENTRY_BUSY; BOOLEAN HeapLocked = FALSE; PHEAP_VIRTUAL_ALLOC_ENTRY VirtualBlock = NULL; PHEAP_ENTRY_EXTRA Extra; NTSTATUS Status;
/* Force flags */ Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugAllocateHeap(Heap, Flags, Size);
/* Check for the maximum size */ if (Size >= 0x80000000) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); DPRINT1("HEAP: Allocation failed!\n"); return NULL; }
if (Flags & (HEAP_CREATE_ENABLE_TRACING)) { DPRINT1("HEAP: RtlAllocateHeap is called with unsupported flags %x, ignoring\n", Flags); }
//DPRINT("RtlAllocateHeap(%p %x %x)\n", Heap, Flags, Size);
/* Calculate allocation size and index */ if (Size) AllocationSize = Size; else AllocationSize = 1; AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
/* Add extra flags in case of settable user value feature is requested, or there is a tag (small or normal) or there is a request to capture stack backtraces */ if ((Flags & HEAP_EXTRA_FLAGS_MASK) || Heap->PseudoTagEntries) { /* Add flag which means that the entry will have extra stuff attached */ EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
/* Account for extra stuff size */ AllocationSize += sizeof(HEAP_ENTRY_EXTRA); }
/* Add settable user flags, if any */ EntryFlags |= (Flags & HEAP_SETTABLE_USER_FLAGS) >> 4;
Index = AllocationSize >> HEAP_ENTRY_SHIFT;
/* Acquire the lock if necessary */ if (!(Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); HeapLocked = TRUE; }
/* Depending on the size, the allocation is going to be done from dedicated, non-dedicated lists or a virtual block of memory */ if (Index <= Heap->VirtualMemoryThreshold) { PHEAP_ENTRY InUseEntry; PHEAP_FREE_ENTRY FreeEntry;
/* First quick check: Anybody here ? */ if (IsListEmpty(&Heap->FreeLists)) return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked);
/* Second quick check: Is there someone for us ? */ FreeEntry = CONTAINING_RECORD(Heap->FreeLists.Blink, HEAP_FREE_ENTRY, FreeList); if (FreeEntry->Size < Index) { /* Largest entry in the list doesnt fit. */ return RtlpAllocateNonDedicated(Heap, Flags, Size, AllocationSize, Index, HeapLocked); }
if (Index > Heap->DeCommitFreeBlockThreshold) { /* Find an entry from the non dedicated list */ FreeEntry = CONTAINING_RECORD(Heap->FreeHints[0], HEAP_FREE_ENTRY, FreeList);
while (FreeEntry->Size < Index) { /* We made sure we had the right size available */ ASSERT(FreeEntry->FreeList.Flink != &Heap->FreeLists); FreeEntry = CONTAINING_RECORD(FreeEntry->FreeList.Flink, HEAP_FREE_ENTRY, FreeList); } } else { /* Get the free entry from the hint */ ULONG HintIndex = RtlFindSetBits(&Heap->FreeHintBitmap, 1, Index - 1); ASSERT(HintIndex != 0xFFFFFFFF); ASSERT((HintIndex >= (Index - 1)) || (HintIndex == 0)); FreeEntry = CONTAINING_RECORD(Heap->FreeHints[HintIndex], HEAP_FREE_ENTRY, FreeList); }
/* Remove the free block, split, profit. */ RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); InUseEntry = RtlpSplitEntry(Heap, Flags, FreeEntry, AllocationSize, Index, Size);
/* Release the lock */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
/* Zero memory if that was requested */ if (Flags & HEAP_ZERO_MEMORY) RtlZeroMemory(InUseEntry + 1, Size); else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) { /* Fill this block with a special pattern */ RtlFillMemoryUlong(InUseEntry + 1, Size & ~0x3, ARENA_INUSE_FILLER); }
/* Fill tail of the block with a special pattern too if requested */ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) { RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, sizeof(HEAP_ENTRY), HEAP_TAIL_FILL); InUseEntry->Flags |= HEAP_ENTRY_FILL_PATTERN; }
/* Prepare extra if it's present */ if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { Extra = RtlpGetExtraStuffPointer(InUseEntry); RtlZeroMemory(Extra, sizeof(HEAP_ENTRY_EXTRA));
// TODO: Tagging }
/* User data starts right after the entry's header */ return InUseEntry + 1; }
if (Heap->Flags & HEAP_GROWABLE) { /* We've got a very big allocation request, satisfy it by directly allocating virtual memory */ AllocationSize += sizeof(HEAP_VIRTUAL_ALLOC_ENTRY) - sizeof(HEAP_ENTRY);
Status = ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID *)&VirtualBlock, 0, &AllocationSize, MEM_COMMIT, PAGE_READWRITE);
if (!NT_SUCCESS(Status)) { // Set STATUS! /* Release the lock */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); DPRINT1("HEAP: Allocation failed!\n"); return NULL; }
/* Initialize the newly allocated block */ VirtualBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size); ASSERT(VirtualBlock->BusyBlock.Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY)); VirtualBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT; VirtualBlock->CommitSize = AllocationSize; VirtualBlock->ReserveSize = AllocationSize;
/* Insert it into the list of virtual allocations */ InsertTailList(&Heap->VirtualAllocdBlocks, &VirtualBlock->Entry);
/* Release the lock */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
/* Return pointer to user data */ return VirtualBlock + 1; }
/* Generate an exception */ if (Flags & HEAP_GENERATE_EXCEPTIONS) { EXCEPTION_RECORD ExceptionRecord;
ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionInformation[0] = AllocationSize;
RtlRaiseException(&ExceptionRecord); }
RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_BUFFER_TOO_SMALL);
/* Release the lock */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); DPRINT1("HEAP: Allocation failed!\n"); return NULL;}
/*********************************************************************** * HeapFree (KERNEL32.338) * RETURNS * TRUE: Success * FALSE: Failure * * @implemented */BOOLEAN NTAPI RtlFreeHeap( HANDLE HeapPtr, /* [in] Handle of heap */ ULONG Flags, /* [in] Heap freeing flags */ PVOID Ptr /* [in] Address of memory to free */){ PHEAP Heap; PHEAP_ENTRY HeapEntry; USHORT TagIndex = 0; SIZE_T BlockSize; PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry; BOOLEAN Locked = FALSE; NTSTATUS Status;
/* Freeing NULL pointer is a legal operation */ if (!Ptr) return TRUE;
/* Get pointer to the heap and force flags */ Heap = (PHEAP)HeapPtr; Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugFreeHeap(Heap, Flags, Ptr);
/* Get pointer to the heap entry */ HeapEntry = (PHEAP_ENTRY)Ptr - 1;
/* Protect with SEH in case the pointer is not valid */ _SEH2_TRY { /* Check this entry, fail if it's invalid */ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY) || (((ULONG_PTR)Ptr & 0x7) != 0) || (HeapEntry->SegmentOffset >= HEAP_SEGMENTS)) { /* This is an invalid block */ DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr); RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); _SEH2_YIELD(return FALSE); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* The pointer was invalid */ DPRINT1("HEAP: Trying to free an invalid address %p!\n", Ptr); RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); _SEH2_YIELD(return FALSE); } _SEH2_END;
/* Lock if necessary */ if (!(Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); Locked = TRUE; }
if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) { /* Big allocation */ VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
/* Remove it from the list */ RemoveEntryList(&VirtualEntry->Entry);
// TODO: Tagging
BlockSize = 0; Status = ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&VirtualEntry, &BlockSize, MEM_RELEASE);
if (!NT_SUCCESS(Status)) { DPRINT1("HEAP: Failed releasing memory with Status 0x%08X. Heap %p, ptr %p, base address %p\n", Status, Heap, Ptr, VirtualEntry); RtlSetLastWin32ErrorAndNtStatusFromNtStatus(Status); } } else { /* Normal allocation */ BlockSize = HeapEntry->Size;
// TODO: Tagging
/* Coalesce in kernel mode, and in usermode if it's not disabled */ if (RtlpGetMode() == KernelMode || (RtlpGetMode() == UserMode && !(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE))) { HeapEntry = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks(Heap, (PHEAP_FREE_ENTRY)HeapEntry, &BlockSize, FALSE); }
/* See if we should decommit this block */ if ((BlockSize >= Heap->DeCommitFreeBlockThreshold) || (Heap->TotalFreeSize + BlockSize >= Heap->DeCommitTotalFreeThreshold)) { RtlpDeCommitFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize); } else { /* Insert into the free list */ RtlpInsertFreeBlock(Heap, (PHEAP_FREE_ENTRY)HeapEntry, BlockSize);
if (RtlpGetMode() == UserMode && TagIndex != 0) { // FIXME: Tagging UNIMPLEMENTED; } } }
/* Release the heap lock */ if (Locked) RtlLeaveHeapLock(Heap->LockVariable);
return TRUE;}
BOOLEAN NTAPIRtlpGrowBlockInPlace (IN PHEAP Heap, IN ULONG Flags, IN PHEAP_ENTRY InUseEntry, IN SIZE_T Size, IN SIZE_T Index){ UCHAR EntryFlags, RememberFlags; PHEAP_FREE_ENTRY FreeEntry, UnusedEntry, FollowingEntry; SIZE_T FreeSize, PrevSize, TailPart, AddedSize = 0; PHEAP_ENTRY_EXTRA OldExtra, NewExtra; UCHAR SegmentOffset;
/* We can't grow beyond specified threshold */ if (Index > Heap->VirtualMemoryThreshold) return FALSE;
/* Get entry flags */ EntryFlags = InUseEntry->Flags;
if (RtlpIsLastCommittedEntry(InUseEntry)) { /* There is no next block, just uncommitted space. Calculate how much is needed */ FreeSize = (Index - InUseEntry->Size) << HEAP_ENTRY_SHIFT; FreeSize = ROUND_UP(FreeSize, PAGE_SIZE);
/* Find and commit those pages */ FreeEntry = RtlpFindAndCommitPages(Heap, Heap->Segments[InUseEntry->SegmentOffset], &FreeSize, (PVOID)PAGE_ROUND_UP(InUseEntry + InUseEntry->Size));
/* Fail if it failed... */ if (!FreeEntry) return FALSE;
/* It was successful, perform coalescing */ FreeSize = FreeSize >> HEAP_ENTRY_SHIFT; FreeEntry = RtlpCoalesceFreeBlocks(Heap, FreeEntry, &FreeSize, FALSE);
/* Check if it's enough */ if (FreeSize + InUseEntry->Size < Index) { /* Still not enough */ RtlpInsertFreeBlock(Heap, FreeEntry, FreeSize); Heap->TotalFreeSize += FreeSize; return FALSE; }
/* Remember flags of this free entry */ RememberFlags = FreeEntry->Flags;
/* Sum up sizes */ FreeSize += InUseEntry->Size; } else { FreeEntry = (PHEAP_FREE_ENTRY)(InUseEntry + InUseEntry->Size);
/* The next block indeed exists. Check if it's free or in use */ if (FreeEntry->Flags & HEAP_ENTRY_BUSY) return FALSE;
/* Next entry is free, check if it can fit the block we need */ FreeSize = InUseEntry->Size + FreeEntry->Size; if (FreeSize < Index) return FALSE;
/* Remember flags of this free entry */ RememberFlags = FreeEntry->Flags;
/* Remove this block from the free list */ RtlpRemoveFreeBlock(Heap, FreeEntry, FALSE); Heap->TotalFreeSize -= FreeEntry->Size; }
PrevSize = (InUseEntry->Size << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes; FreeSize -= Index;
/* Don't produce too small blocks */ if (FreeSize <= 2) { Index += FreeSize; FreeSize = 0; }
/* Process extra stuff */ if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT) { /* Calculate pointers */ OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1); NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1);
/* Copy contents */ *NewExtra = *OldExtra;
// FIXME Tagging }
/* Update sizes */ InUseEntry->Size = (USHORT)Index; InUseEntry->UnusedBytes = (UCHAR)((Index << HEAP_ENTRY_SHIFT) - Size);
/* Check if there is a free space remaining after merging those blocks */ if (!FreeSize) { /* Update flags and sizes */ InUseEntry->Flags |= RememberFlags & HEAP_ENTRY_LAST_ENTRY;
/* Either update previous size of the next entry or mark it as a last entry in the segment */ if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY)) { (InUseEntry + InUseEntry->Size)->PreviousSize = InUseEntry->Size; } else { SegmentOffset = InUseEntry->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS); } } else { /* Complex case, we need to split the block to give unused free space back to the heap */ UnusedEntry = (PHEAP_FREE_ENTRY)(InUseEntry + Index); UnusedEntry->PreviousSize = (USHORT)Index; UnusedEntry->SegmentOffset = InUseEntry->SegmentOffset;
/* Update the following block or set the last entry in the segment */ if (RememberFlags & HEAP_ENTRY_LAST_ENTRY) { SegmentOffset = UnusedEntry->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS);
/* Set flags and size */ UnusedEntry->Flags = RememberFlags; UnusedEntry->Size = (USHORT)FreeSize;
/* Insert it to the heap and update total size */ RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE); Heap->TotalFreeSize += FreeSize; } else { /* There is a block after this one */ FollowingEntry = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)UnusedEntry + FreeSize);
if (FollowingEntry->Flags & HEAP_ENTRY_BUSY) { /* Update flags and set size of the unused space entry */ UnusedEntry->Flags = RememberFlags & (~HEAP_ENTRY_LAST_ENTRY); UnusedEntry->Size = (USHORT)FreeSize;
/* Update previous size of the following entry */ FollowingEntry->PreviousSize = (USHORT)FreeSize;
/* Insert it to the heap and update total free size */ RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE); Heap->TotalFreeSize += FreeSize; } else { /* That following entry is also free, what a fortune! */ RememberFlags = FollowingEntry->Flags;
/* Remove it */ RtlpRemoveFreeBlock(Heap, FollowingEntry, FALSE); Heap->TotalFreeSize -= FollowingEntry->Size;
/* And make up a new combined block */ FreeSize += FollowingEntry->Size; UnusedEntry->Flags = RememberFlags;
/* Check where to put it */ if (FreeSize <= HEAP_MAX_BLOCK_SIZE) { /* Fine for a dedicated list */ UnusedEntry->Size = (USHORT)FreeSize;
if (!(RememberFlags & HEAP_ENTRY_LAST_ENTRY)) { ((PHEAP_ENTRY)UnusedEntry + FreeSize)->PreviousSize = (USHORT)FreeSize; } else { SegmentOffset = UnusedEntry->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS); }
/* Insert it back and update total size */ RtlpInsertFreeBlockHelper(Heap, UnusedEntry, FreeSize, FALSE); Heap->TotalFreeSize += FreeSize; } else { /* The block is very large, leave all the hassle to the insertion routine */ RtlpInsertFreeBlock(Heap, UnusedEntry, FreeSize); } } } }
/* Properly "zero out" (and fill!) the space */ if (Flags & HEAP_ZERO_MEMORY) { RtlZeroMemory((PCHAR)(InUseEntry + 1) + PrevSize, Size - PrevSize); } else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) { /* Calculate tail part which we need to fill */ TailPart = PrevSize & (sizeof(ULONG) - 1);
/* "Invert" it as usual */ if (TailPart) TailPart = 4 - TailPart;
if (Size > (PrevSize + TailPart)) AddedSize = (Size - (PrevSize + TailPart)) & ~(sizeof(ULONG) - 1);
if (AddedSize) { RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + PrevSize + TailPart, AddedSize, ARENA_INUSE_FILLER); } }
/* Fill the new tail */ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) { RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, HEAP_ENTRY_SIZE, HEAP_TAIL_FILL); }
/* Copy user settable flags */ InUseEntry->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS; InUseEntry->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
/* Return success */ return TRUE;}
PHEAP_ENTRY_EXTRA NTAPIRtlpGetExtraStuffPointer(PHEAP_ENTRY HeapEntry){ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualEntry;
/* Check if it's a big block */ if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) { VirtualEntry = CONTAINING_RECORD(HeapEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
/* Return a pointer to the extra stuff*/ return &VirtualEntry->ExtraStuff; } else { /* This is a usual entry, which means extra stuff follows this block */ return (PHEAP_ENTRY_EXTRA)(HeapEntry + HeapEntry->Size - 1); }}
/*********************************************************************** * RtlReAllocateHeap * PARAMS * Heap [in] Handle of heap block * Flags [in] Heap reallocation flags * Ptr, [in] Address of memory to reallocate * Size [in] Number of bytes to reallocate * * RETURNS * Pointer to reallocated memory block * NULL: Failure * 0x7d030f60--invalid flags in RtlHeapAllocate * @implemented */PVOID NTAPIRtlReAllocateHeap(HANDLE HeapPtr, ULONG Flags, PVOID Ptr, SIZE_T Size){ PHEAP Heap = (PHEAP)HeapPtr; PHEAP_ENTRY InUseEntry, NewInUseEntry; PHEAP_ENTRY_EXTRA OldExtra, NewExtra; SIZE_T AllocationSize, FreeSize, DecommitSize; BOOLEAN HeapLocked = FALSE; PVOID NewBaseAddress; PHEAP_FREE_ENTRY SplitBlock, SplitBlock2; SIZE_T OldSize, Index, OldIndex; UCHAR FreeFlags; NTSTATUS Status; PVOID DecommitBase; SIZE_T RemainderBytes, ExtraSize; PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock; EXCEPTION_RECORD ExceptionRecord; UCHAR SegmentOffset;
/* Return success in case of a null pointer */ if (!Ptr) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_SUCCESS); return NULL; }
/* Force heap flags */ Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugReAllocateHeap(Heap, Flags, Ptr, Size);
/* Make sure size is valid */ if (Size >= 0x80000000) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); return NULL; }
/* Calculate allocation size and index */ if (Size) AllocationSize = Size; else AllocationSize = 1; AllocationSize = (AllocationSize + Heap->AlignRound) & Heap->AlignMask;
/* Add up extra stuff, if it is present anywhere */ if (((((PHEAP_ENTRY)Ptr)-1)->Flags & HEAP_ENTRY_EXTRA_PRESENT) || (Flags & HEAP_EXTRA_FLAGS_MASK) || Heap->PseudoTagEntries) { AllocationSize += sizeof(HEAP_ENTRY_EXTRA); }
/* Acquire the lock if necessary */ if (!(Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); HeapLocked = TRUE; /* Do not acquire the lock anymore for re-entrant call */ Flags |= HEAP_NO_SERIALIZE; }
/* Get the pointer to the in-use entry */ InUseEntry = (PHEAP_ENTRY)Ptr - 1;
/* If that entry is not really in-use, we have a problem */ if (!(InUseEntry->Flags & HEAP_ENTRY_BUSY)) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
/* Release the lock and return */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable); return Ptr; }
if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) { /* This is a virtually allocated block. Get its size */ OldSize = RtlpGetSizeOfBigBlock(InUseEntry);
/* Convert it to an index */ OldIndex = (OldSize + InUseEntry->Size) >> HEAP_ENTRY_SHIFT;
/* Calculate new allocation size and round it to the page size */ AllocationSize += FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock); AllocationSize = ROUND_UP(AllocationSize, PAGE_SIZE); } else { /* Usual entry */ OldIndex = InUseEntry->Size;
OldSize = (OldIndex << HEAP_ENTRY_SHIFT) - InUseEntry->UnusedBytes; }
/* Calculate new index */ Index = AllocationSize >> HEAP_ENTRY_SHIFT;
/* Check for 4 different scenarios (old size, new size, old index, new index) */ if (Index <= OldIndex) { /* Difference must be greater than 1, adjust if it's not so */ if (Index + 1 == OldIndex) { Index++; AllocationSize += sizeof(HEAP_ENTRY); }
/* Calculate new size */ if (InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) { /* Simple in case of a virtual alloc - just an unused size */ InUseEntry->Size = (USHORT)(AllocationSize - Size); ASSERT(InUseEntry->Size >= sizeof(HEAP_VIRTUAL_ALLOC_ENTRY)); } else if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { /* There is extra stuff, take it into account */ OldExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + InUseEntry->Size - 1); NewExtra = (PHEAP_ENTRY_EXTRA)(InUseEntry + Index - 1); *NewExtra = *OldExtra;
// FIXME Tagging, TagIndex
/* Update unused bytes count */ InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size); } else { // FIXME Tagging, SmallTagIndex InUseEntry->UnusedBytes = (UCHAR)(AllocationSize - Size); }
/* If new size is bigger than the old size */ if (Size > OldSize) { /* Zero out that additional space if required */ if (Flags & HEAP_ZERO_MEMORY) { RtlZeroMemory((PCHAR)Ptr + OldSize, Size - OldSize); } else if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) { /* Fill it on free if required */ RemainderBytes = OldSize & (sizeof(ULONG) - 1);
if (RemainderBytes) RemainderBytes = 4 - RemainderBytes;
if (Size > (OldSize + RemainderBytes)) { /* Calculate actual amount of extra bytes to fill */ ExtraSize = (Size - (OldSize + RemainderBytes)) & ~(sizeof(ULONG) - 1);
/* Fill them if there are any */ if (ExtraSize != 0) { RtlFillMemoryUlong((PCHAR)(InUseEntry + 1) + OldSize + RemainderBytes, ExtraSize, ARENA_INUSE_FILLER); } } } }
/* Fill tail of the heap entry if required */ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) { RtlFillMemory((PCHAR)(InUseEntry + 1) + Size, HEAP_ENTRY_SIZE, HEAP_TAIL_FILL); }
/* Check if the difference is significant or not */ if (Index != OldIndex) { /* Save flags */ FreeFlags = InUseEntry->Flags & ~HEAP_ENTRY_BUSY;
if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC) { /* This is a virtual block allocation */ VirtualAllocBlock = CONTAINING_RECORD(InUseEntry, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock);
// FIXME Tagging!
DecommitBase = (PCHAR)VirtualAllocBlock + AllocationSize; DecommitSize = (OldIndex << HEAP_ENTRY_SHIFT) - AllocationSize;
/* Release the memory */ Status = ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&DecommitBase, &DecommitSize, MEM_RELEASE);
if (!NT_SUCCESS(Status)) { DPRINT1("HEAP: Unable to release memory (pointer %p, size 0x%x), Status %08x\n", DecommitBase, DecommitSize, Status); } else { /* Otherwise reduce the commit size */ VirtualAllocBlock->CommitSize -= DecommitSize; } } else { /* Reduce size of the block and possibly split it */ SplitBlock = (PHEAP_FREE_ENTRY)(InUseEntry + Index);
/* Initialize this entry */ SplitBlock->Flags = FreeFlags; SplitBlock->PreviousSize = (USHORT)Index; SplitBlock->SegmentOffset = InUseEntry->SegmentOffset;
/* Remember free size */ FreeSize = InUseEntry->Size - Index;
/* Set new size */ InUseEntry->Size = (USHORT)Index; InUseEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
/* Is that the last entry */ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) { SegmentOffset = SplitBlock->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS);
/* Set its size and insert it to the list */ SplitBlock->Size = (USHORT)FreeSize; RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
/* Update total free size */ Heap->TotalFreeSize += FreeSize; } else { /* Get the block after that one */ SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) { /* It's in use, add it here*/ SplitBlock->Size = (USHORT)FreeSize;
/* Update previous size of the next entry */ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
/* Insert it to the list */ RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE);
/* Update total size */ Heap->TotalFreeSize += FreeSize; } else { /* Next entry is free, so merge with it */ SplitBlock->Flags = SplitBlock2->Flags;
/* Remove it, update total size */ RtlpRemoveFreeBlock(Heap, SplitBlock2, FALSE); Heap->TotalFreeSize -= SplitBlock2->Size;
/* Calculate total free size */ FreeSize += SplitBlock2->Size;
if (FreeSize <= HEAP_MAX_BLOCK_SIZE) { SplitBlock->Size = (USHORT)FreeSize;
if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) { /* Update previous size of the next entry */ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize; } else { SegmentOffset = SplitBlock->SegmentOffset; ASSERT(SegmentOffset < HEAP_SEGMENTS); }
/* Insert the new one back and update total size */ RtlpInsertFreeBlockHelper(Heap, SplitBlock, FreeSize, FALSE); Heap->TotalFreeSize += FreeSize; } else { /* Just add it */ RtlpInsertFreeBlock(Heap, SplitBlock, FreeSize); } } } } } } else { /* We're growing the block */ if ((InUseEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) || !RtlpGrowBlockInPlace(Heap, Flags, InUseEntry, Size, Index)) { /* Growing in place failed, so growing out of place */ if (Flags & HEAP_REALLOC_IN_PLACE_ONLY) { DPRINT1("Realloc in place failed, but it was the only option\n"); Ptr = NULL; } else { /* Clear tag bits */ Flags &= ~HEAP_TAG_MASK;
/* Process extra stuff */ if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { /* Preserve user settable flags */ Flags &= ~HEAP_SETTABLE_USER_FLAGS;
Flags |= HEAP_SETTABLE_USER_VALUE | ((InUseEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
/* Get pointer to the old extra data */ OldExtra = RtlpGetExtraStuffPointer(InUseEntry);
/* Save tag index if it was set */ if (OldExtra->TagIndex && !(OldExtra->TagIndex & HEAP_PSEUDO_TAG_FLAG)) { Flags |= OldExtra->TagIndex << HEAP_TAG_SHIFT; } } else if (InUseEntry->SmallTagIndex) { /* Take small tag index into account */ Flags |= InUseEntry->SmallTagIndex << HEAP_TAG_SHIFT; }
/* Allocate new block from the heap */ NewBaseAddress = RtlAllocateHeap(HeapPtr, Flags & ~HEAP_ZERO_MEMORY, Size);
/* Proceed if it didn't fail */ if (NewBaseAddress) { /* Get new entry pointer */ NewInUseEntry = (PHEAP_ENTRY)NewBaseAddress - 1;
/* Process extra stuff if it exists */ if (NewInUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { NewExtra = RtlpGetExtraStuffPointer(NewInUseEntry);
if (InUseEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { OldExtra = RtlpGetExtraStuffPointer(InUseEntry); NewExtra->Settable = OldExtra->Settable; } else { RtlZeroMemory(NewExtra, sizeof(*NewExtra)); } }
/* Copy actual user bits */ if (Size < OldSize) RtlMoveMemory(NewBaseAddress, Ptr, Size); else RtlMoveMemory(NewBaseAddress, Ptr, OldSize);
/* Zero remaining part if required */ if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) { RtlZeroMemory((PCHAR)NewBaseAddress + OldSize, Size - OldSize); }
/* Free the old block */ RtlFreeHeap(HeapPtr, Flags, Ptr); }
Ptr = NewBaseAddress; } } }
/* Did resizing fail? */ if (!Ptr && (Flags & HEAP_GENERATE_EXCEPTIONS)) { /* Generate an exception if required */ ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionInformation[0] = AllocationSize;
RtlRaiseException(&ExceptionRecord); }
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return Ptr;}
/*********************************************************************** * RtlCompactHeap * * @unimplemented */ULONG NTAPIRtlCompactHeap(HANDLE Heap, ULONG Flags){ UNIMPLEMENTED; return 0;}
/*********************************************************************** * RtlLockHeap * Attempts to acquire the critical section object for a specified heap. * * PARAMS * Heap [in] Handle of heap to lock for exclusive access * * RETURNS * TRUE: Success * FALSE: Failure * * @implemented */BOOLEAN NTAPIRtlLockHeap(IN HANDLE HeapPtr){ PHEAP Heap = (PHEAP)HeapPtr;
/* Check for page heap */ if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) { return RtlpPageHeapLock(Heap); }
/* Check if it's really a heap */ if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
/* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); }
return TRUE;}
/*********************************************************************** * RtlUnlockHeap * Releases ownership of the critical section object. * * PARAMS * Heap [in] Handle to the heap to unlock * * RETURNS * TRUE: Success * FALSE: Failure * * @implemented */BOOLEAN NTAPIRtlUnlockHeap(HANDLE HeapPtr){ PHEAP Heap = (PHEAP)HeapPtr;
/* Check for page heap */ if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) { return RtlpPageHeapUnlock(Heap); }
/* Check if it's really a heap */ if (Heap->Signature != HEAP_SIGNATURE) return FALSE;
/* Unlock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { RtlLeaveHeapLock(Heap->LockVariable); }
return TRUE;}
/*********************************************************************** * RtlSizeHeap * PARAMS * Heap [in] Handle of heap * Flags [in] Heap size control flags * Ptr [in] Address of memory to return size for * * RETURNS * Size in bytes of allocated memory * 0xffffffff: Failure * * @implemented */SIZE_T NTAPIRtlSizeHeap( HANDLE HeapPtr, ULONG Flags, PVOID Ptr){ PHEAP Heap = (PHEAP)HeapPtr; PHEAP_ENTRY HeapEntry; SIZE_T EntrySize;
// FIXME This is a hack around missing SEH support! if (!Heap) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_HANDLE); return (SIZE_T)-1; }
/* Force flags */ Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugSizeHeap(Heap, Flags, Ptr);
/* Get the heap entry pointer */ HeapEntry = (PHEAP_ENTRY)Ptr - 1;
/* Return -1 if that entry is free */ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); return (SIZE_T)-1; }
/* Get size of this block depending if it's a usual or a big one */ if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) { EntrySize = RtlpGetSizeOfBigBlock(HeapEntry); } else { /* Calculate it */ EntrySize = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes; }
/* Return calculated size */ return EntrySize;}
BOOLEAN NTAPIRtlpCheckInUsePattern(PHEAP_ENTRY HeapEntry){ SIZE_T Size, Result; PCHAR TailPart;
/* Calculate size */ if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) Size = RtlpGetSizeOfBigBlock(HeapEntry); else Size = (HeapEntry->Size << HEAP_ENTRY_SHIFT) - HeapEntry->UnusedBytes;
/* Calculate pointer to the tail part of the block */ TailPart = (PCHAR)(HeapEntry + 1) + Size;
/* Compare tail pattern */ Result = RtlCompareMemory(TailPart, FillPattern, HEAP_ENTRY_SIZE);
if (Result != HEAP_ENTRY_SIZE) { DPRINT1("HEAP: Heap entry (size %x) %p tail is modified at %p\n", Size, HeapEntry, TailPart + Result); return FALSE; }
/* All is fine */ return TRUE;}
BOOLEAN NTAPIRtlpValidateHeapHeaders( PHEAP Heap, BOOLEAN Recalculate){ // We skip header validation for now return TRUE;}
BOOLEAN NTAPIRtlpValidateHeapEntry( PHEAP Heap, PHEAP_ENTRY HeapEntry){ BOOLEAN BigAllocation, EntryFound = FALSE; PHEAP_SEGMENT Segment; ULONG SegmentOffset;
/* Perform various consistency checks of this entry */ if (!HeapEntry) goto invalid_entry; if ((ULONG_PTR)HeapEntry & (HEAP_ENTRY_SIZE - 1)) goto invalid_entry; if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) goto invalid_entry;
BigAllocation = HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC; Segment = Heap->Segments[HeapEntry->SegmentOffset];
if (BigAllocation && (((ULONG_PTR)HeapEntry & (PAGE_SIZE - 1)) != FIELD_OFFSET(HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock))) goto invalid_entry;
if (!BigAllocation && (HeapEntry->SegmentOffset >= HEAP_SEGMENTS || !Segment || HeapEntry < Segment->FirstEntry || HeapEntry >= Segment->LastValidEntry)) goto invalid_entry;
if ((HeapEntry->Flags & HEAP_ENTRY_FILL_PATTERN) && !RtlpCheckInUsePattern(HeapEntry)) goto invalid_entry;
/* Checks are done, if this is a virtual entry, that's all */ if (HeapEntry->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) return TRUE;
/* Go through segments and check if this entry fits into any of them */ for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++) { Segment = Heap->Segments[SegmentOffset]; if (!Segment) continue;
if ((HeapEntry >= Segment->FirstEntry) && (HeapEntry < Segment->LastValidEntry)) { /* Got it */ EntryFound = TRUE; break; } }
/* Return our result of finding entry in the segments */ return EntryFound;
invalid_entry: DPRINT1("HEAP: Invalid heap entry %p in heap %p\n", HeapEntry, Heap); return FALSE;}
BOOLEAN NTAPIRtlpValidateHeapSegment( PHEAP Heap, PHEAP_SEGMENT Segment, UCHAR SegmentOffset, PULONG FreeEntriesCount, PSIZE_T TotalFreeSize, PSIZE_T TagEntries, PSIZE_T PseudoTagEntries){ PHEAP_UCR_DESCRIPTOR UcrDescriptor; PLIST_ENTRY UcrEntry; SIZE_T ByteSize, Size, Result; PHEAP_ENTRY CurrentEntry; ULONG UnCommittedPages; ULONG UnCommittedRanges; ULONG PreviousSize;
UnCommittedPages = 0; UnCommittedRanges = 0;
if (IsListEmpty(&Segment->UCRSegmentList)) { UcrEntry = NULL; UcrDescriptor = NULL; } else { UcrEntry = Segment->UCRSegmentList.Flink; UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry); }
if (Segment->BaseAddress == Heap) CurrentEntry = &Heap->Entry; else CurrentEntry = &Segment->Entry;
while (CurrentEntry < Segment->LastValidEntry) { if (UcrDescriptor && ((PVOID)CurrentEntry >= UcrDescriptor->Address)) { DPRINT1("HEAP: Entry %p is not inside uncommited range [%p .. %p)\n", CurrentEntry, UcrDescriptor->Address, (PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
return FALSE; }
PreviousSize = 0;
while (CurrentEntry < Segment->LastValidEntry) { if (PreviousSize != CurrentEntry->PreviousSize) { DPRINT1("HEAP: Entry %p has incorrect PreviousSize %x instead of %x\n", CurrentEntry, CurrentEntry->PreviousSize, PreviousSize);
return FALSE; }
PreviousSize = CurrentEntry->Size; Size = CurrentEntry->Size << HEAP_ENTRY_SHIFT;
if (CurrentEntry->Flags & HEAP_ENTRY_BUSY) { if (TagEntries) { UNIMPLEMENTED; }
/* Check fill pattern */ if (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN) { if (!RtlpCheckInUsePattern(CurrentEntry)) return FALSE; } } else { /* The entry is free, increase free entries count and total free size */ *FreeEntriesCount = *FreeEntriesCount + 1; *TotalFreeSize += CurrentEntry->Size;
if ((Heap->Flags & HEAP_FREE_CHECKING_ENABLED) && (CurrentEntry->Flags & HEAP_ENTRY_FILL_PATTERN)) { ByteSize = Size - sizeof(HEAP_FREE_ENTRY);
if ((CurrentEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) && (ByteSize > sizeof(HEAP_FREE_ENTRY_EXTRA))) { ByteSize -= sizeof(HEAP_FREE_ENTRY_EXTRA); }
Result = RtlCompareMemoryUlong((PCHAR)((PHEAP_FREE_ENTRY)CurrentEntry + 1), ByteSize, ARENA_FREE_FILLER);
if (Result != ByteSize) { DPRINT1("HEAP: Free heap block %p modified at %p after it was freed\n", CurrentEntry, (PCHAR)(CurrentEntry + 1) + Result);
return FALSE; } } }
if (CurrentEntry->SegmentOffset != SegmentOffset) { DPRINT1("HEAP: Heap entry %p SegmentOffset is incorrect %x (should be %x)\n", CurrentEntry, SegmentOffset, CurrentEntry->SegmentOffset); return FALSE; }
/* Check if it's the last entry */ if (CurrentEntry->Flags & HEAP_ENTRY_LAST_ENTRY) { CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size);
if (!UcrDescriptor) { /* Check if it's not really the last one */ if (CurrentEntry != Segment->LastValidEntry) { DPRINT1("HEAP: Heap entry %p is not last block in segment (%p)\n", CurrentEntry, Segment->LastValidEntry); return FALSE; } } else if (CurrentEntry != UcrDescriptor->Address) { DPRINT1("HEAP: Heap entry %p does not match next uncommitted address (%p)\n", CurrentEntry, UcrDescriptor->Address);
return FALSE; } else { UnCommittedPages += (ULONG)(UcrDescriptor->Size / PAGE_SIZE); UnCommittedRanges++;
CurrentEntry = (PHEAP_ENTRY)((PCHAR)UcrDescriptor->Address + UcrDescriptor->Size);
/* Go to the next UCR descriptor */ UcrEntry = UcrEntry->Flink; if (UcrEntry == &Segment->UCRSegmentList) { UcrEntry = NULL; UcrDescriptor = NULL; } else { UcrDescriptor = CONTAINING_RECORD(UcrEntry, HEAP_UCR_DESCRIPTOR, SegmentEntry); } }
break; }
/* Advance to the next entry */ CurrentEntry = (PHEAP_ENTRY)((PCHAR)CurrentEntry + Size); } }
/* Check total numbers of UCP and UCR */ if (Segment->NumberOfUnCommittedPages != UnCommittedPages) { DPRINT1("HEAP: Segment %p NumberOfUnCommittedPages is invalid (%x != %x)\n", Segment, Segment->NumberOfUnCommittedPages, UnCommittedPages);
return FALSE; }
if (Segment->NumberOfUnCommittedRanges != UnCommittedRanges) { DPRINT1("HEAP: Segment %p NumberOfUnCommittedRanges is invalid (%x != %x)\n", Segment, Segment->NumberOfUnCommittedRanges, UnCommittedRanges);
return FALSE; }
return TRUE;}
BOOLEAN NTAPIRtlpValidateHeap(PHEAP Heap, BOOLEAN ForceValidation){ UCHAR SegmentOffset; SIZE_T TotalFreeSize; PLIST_ENTRY ListHead, NextEntry; ULONG FreeBlocksCount, FreeListEntriesCount; ULONG HintIndex;
/* Check headers */ if (!RtlpValidateHeapHeaders(Heap, FALSE)) return FALSE;
/* Skip validation if it's not needed */ if (!ForceValidation && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED)) return TRUE;
/* Check free list */ FreeListEntriesCount = 0; ListHead = &Heap->FreeLists; NextEntry = ListHead->Flink;
while (NextEntry != ListHead) { PHEAP_FREE_ENTRY FreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList);
NextEntry = NextEntry->Flink;
if (NextEntry != ListHead) { PHEAP_FREE_ENTRY NextFreeEntry = CONTAINING_RECORD(NextEntry, HEAP_FREE_ENTRY, FreeList); /* Free entries must be sorted */ if (FreeEntry->Size > NextFreeEntry->Size) { DPRINT1("Dedicated free entry %p of size %ld is not put in order.\n", FreeEntry, FreeEntry->Size); } }
/* Check that the hint is there */ if (FreeEntry->Size > Heap->DeCommitFreeBlockThreshold) { if (Heap->FreeHints[0] == NULL) { DPRINT1("No hint pointing to the non-dedicated list although there is a free entry %p of size %ld.\n", FreeEntry, FreeEntry->Size); } if (!RtlTestBit(&Heap->FreeHintBitmap, 0)) { DPRINT1("Hint bit 0 is not set although there is a free entry %p of size %ld.\n", FreeEntry, FreeEntry->Size); } } else { if (Heap->FreeHints[FreeEntry->Size - 1] == NULL) { DPRINT1("No hint pointing to the dedicated list although there is a free entry %p of size %ld.\n", FreeEntry, FreeEntry->Size); } if (!RtlTestBit(&Heap->FreeHintBitmap, FreeEntry->Size - 1)) { DPRINT1("Hint bit 0 is not set although there is a free entry %p of size %ld.\n", FreeEntry, FreeEntry->Size); } }
/* If there is an in-use entry in a free list - that's quite a big problem */ if (FreeEntry->Flags & HEAP_ENTRY_BUSY) { DPRINT1("HEAP: Free element %p is marked in-use\n", FreeEntry); return FALSE; }
/* Add up to the total amount of free entries */ FreeListEntriesCount++; }
/* Check free list hints */ for (HintIndex = 0; HintIndex < Heap->DeCommitFreeBlockThreshold; HintIndex++) { if (Heap->FreeHints[HintIndex] != NULL) { PHEAP_FREE_ENTRY FreeEntry = CONTAINING_RECORD(Heap->FreeHints[HintIndex], HEAP_FREE_ENTRY, FreeList);
if (!RtlTestBit(&Heap->FreeHintBitmap, HintIndex)) { DPRINT1("Hint bitmap bit at %u is not set, but there is a hint entry.\n", HintIndex); }
if (HintIndex == 0) { if (FreeEntry->Size <= Heap->DeCommitFreeBlockThreshold) { DPRINT1("There is an entry %p of size %lu, smaller than the decommit threshold %lu in the non-dedicated free list hint.\n", FreeEntry, FreeEntry->Size, Heap->DeCommitFreeBlockThreshold); } } else { if (HintIndex != FreeEntry->Size - 1) { DPRINT1("There is an entry %p of size %lu at the position %u in the free entry hint array.\n", FreeEntry, FreeEntry->Size, HintIndex); }
if (FreeEntry->FreeList.Blink != &Heap->FreeLists) { /* The entry right before the hint must be smaller. */ PHEAP_FREE_ENTRY PreviousFreeEntry = CONTAINING_RECORD(FreeEntry->FreeList.Blink, HEAP_FREE_ENTRY, FreeList); if (PreviousFreeEntry->Size >= FreeEntry->Size) { DPRINT1("Free entry hint %p of size %lu is larger than the entry before it %p, which is of size %lu.\n", FreeEntry, FreeEntry->Size, PreviousFreeEntry, PreviousFreeEntry->Size); } } } } else if (RtlTestBit(&Heap->FreeHintBitmap, HintIndex)) { DPRINT1("Hint bitmap bit at %u is set, but there is no hint entry.\n", HintIndex); } }
/* Check big allocations */ ListHead = &Heap->VirtualAllocdBlocks; NextEntry = ListHead->Flink;
while (ListHead != NextEntry) { PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock = CONTAINING_RECORD(NextEntry, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
/* We can only check the fill pattern */ if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN) { if (!RtlpCheckInUsePattern(&VirtualAllocBlock->BusyBlock)) return FALSE; }
NextEntry = NextEntry->Flink; }
/* Check all segments */ FreeBlocksCount = 0; TotalFreeSize = 0;
for (SegmentOffset = 0; SegmentOffset < HEAP_SEGMENTS; SegmentOffset++) { PHEAP_SEGMENT Segment = Heap->Segments[SegmentOffset];
/* Go to the next one if there is no segment */ if (!Segment) continue;
if (!RtlpValidateHeapSegment(Heap, Segment, SegmentOffset, &FreeBlocksCount, &TotalFreeSize, NULL, NULL)) { return FALSE; } }
if (FreeListEntriesCount != FreeBlocksCount) { DPRINT1("HEAP: Free blocks count in arena (%lu) does not match free blocks number in the free lists (%lu)\n", FreeBlocksCount, FreeListEntriesCount); return FALSE; }
if (Heap->TotalFreeSize != TotalFreeSize) { DPRINT1("HEAP: Total size of free blocks in arena (%Iu) does not equal to the one in heap header (%Iu)\n", TotalFreeSize, Heap->TotalFreeSize); return FALSE; }
return TRUE;}
/*********************************************************************** * RtlValidateHeap * Validates a specified heap. * * PARAMS * Heap [in] Handle to the heap * Flags [in] Bit flags that control access during operation * Block [in] Optional pointer to memory block to validate * * NOTES * Flags is ignored. * * RETURNS * TRUE: Success * FALSE: Failure * * @implemented */BOOLEAN NTAPI RtlValidateHeap( HANDLE HeapPtr, ULONG Flags, PVOID Block){ PHEAP Heap = (PHEAP)HeapPtr; BOOLEAN HeapLocked = FALSE; BOOLEAN HeapValid;
/* Check for page heap */ if (Heap->ForceFlags & HEAP_FLAG_PAGE_ALLOCS) return RtlpDebugPageHeapValidate(HeapPtr, Flags, Block);
/* Check signature */ if (Heap->Signature != HEAP_SIGNATURE) { DPRINT1("HEAP: Signature %lx is invalid for heap %p\n", Heap->Signature, Heap); return FALSE; }
/* Force flags */ Flags |= Heap->ForceFlags;
/* Acquire the lock if necessary */ if (!(Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); HeapLocked = TRUE; }
/* Either validate whole heap or just one entry */ if (!Block) HeapValid = RtlpValidateHeap(Heap, TRUE); else HeapValid = RtlpValidateHeapEntry(Heap, (PHEAP_ENTRY)Block - 1);
/* Unlock if it's lockable */ if (HeapLocked) { RtlLeaveHeapLock(Heap->LockVariable); }
return HeapValid;}
/* * @unimplemented */NTSTATUS NTAPIRtlEnumProcessHeaps(PHEAP_ENUMERATION_ROUTINE HeapEnumerationRoutine, PVOID lParam){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
/* * @unimplemented */BOOLEAN NTAPIRtlValidateProcessHeaps(VOID){ UNIMPLEMENTED; return TRUE;}
/* * @unimplemented */BOOLEAN NTAPIRtlZeroHeap( IN PVOID HeapHandle, IN ULONG Flags ){ UNIMPLEMENTED; return FALSE;}
/* * @implemented */BOOLEANNTAPIRtlSetUserValueHeap(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, IN PVOID UserValue){ PHEAP Heap = (PHEAP)HeapHandle; PHEAP_ENTRY HeapEntry; PHEAP_ENTRY_EXTRA Extra; BOOLEAN HeapLocked = FALSE, ValueSet = FALSE;
/* Force flags */ Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugSetUserValueHeap(Heap, Flags, BaseAddress, UserValue);
/* Lock if it's lockable */ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); HeapLocked = TRUE; }
/* Get a pointer to the entry */ HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
/* If it's a free entry - return error */ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return FALSE; }
/* Check if this entry has an extra stuff associated with it */ if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { /* Use extra to store the value */ Extra = RtlpGetExtraStuffPointer(HeapEntry); Extra->Settable = (ULONG_PTR)UserValue;
/* Indicate that value was set */ ValueSet = TRUE; }
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return ValueSet;}
/* * @implemented */BOOLEANNTAPIRtlSetUserFlagsHeap(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, IN ULONG UserFlagsReset, IN ULONG UserFlagsSet){ PHEAP Heap = (PHEAP)HeapHandle; PHEAP_ENTRY HeapEntry; BOOLEAN HeapLocked = FALSE;
/* Force flags */ Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugSetUserFlagsHeap(Heap, Flags, BaseAddress, UserFlagsReset, UserFlagsSet);
/* Lock if it's lockable */ if (!(Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); HeapLocked = TRUE; }
/* Get a pointer to the entry */ HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
/* If it's a free entry - return error */ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return FALSE; }
/* Set / reset flags */ HeapEntry->Flags &= ~(UserFlagsReset >> 4); HeapEntry->Flags |= (UserFlagsSet >> 4);
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return TRUE;}
/* * @implemented */BOOLEANNTAPIRtlGetUserInfoHeap(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, OUT PVOID *UserValue, OUT PULONG UserFlags){ PHEAP Heap = (PHEAP)HeapHandle; PHEAP_ENTRY HeapEntry; PHEAP_ENTRY_EXTRA Extra; BOOLEAN HeapLocked = FALSE;
/* Force flags */ Flags |= Heap->ForceFlags;
/* Call special heap */ if (RtlpHeapIsSpecial(Flags)) return RtlDebugGetUserInfoHeap(Heap, Flags, BaseAddress, UserValue, UserFlags);
/* Lock if it's lockable */ if (!(Flags & HEAP_NO_SERIALIZE)) { RtlEnterHeapLock(Heap->LockVariable, TRUE); HeapLocked = TRUE; }
/* Get a pointer to the entry */ HeapEntry = (PHEAP_ENTRY)BaseAddress - 1;
/* If it's a free entry - return error */ if (!(HeapEntry->Flags & HEAP_ENTRY_BUSY)) { RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER);
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return FALSE; }
/* Check if this entry has an extra stuff associated with it */ if (HeapEntry->Flags & HEAP_ENTRY_EXTRA_PRESENT) { /* Get pointer to extra data */ Extra = RtlpGetExtraStuffPointer(HeapEntry);
/* Pass user value */ if (UserValue) *UserValue = (PVOID)Extra->Settable; }
/* Decode and return user flags */ if (UserFlags) *UserFlags = (HeapEntry->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
/* Release the heap lock if it was acquired */ if (HeapLocked) RtlLeaveHeapLock(Heap->LockVariable);
return TRUE;}
/* * @unimplemented */NTSTATUSNTAPIRtlUsageHeap(IN HANDLE Heap, IN ULONG Flags, OUT PRTL_HEAP_USAGE Usage){ /* TODO */ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
PWSTRNTAPIRtlQueryTagHeap(IN PVOID HeapHandle, IN ULONG Flags, IN USHORT TagIndex, IN BOOLEAN ResetCounters, OUT PRTL_HEAP_TAG_INFO HeapTagInfo){ /* TODO */ UNIMPLEMENTED; return NULL;}
ULONGNTAPIRtlExtendHeap(IN HANDLE Heap, IN ULONG Flags, IN PVOID P, IN SIZE_T Size){ /* TODO */ UNIMPLEMENTED; return 0;}
ULONGNTAPIRtlCreateTagHeap(_In_ HANDLE HeapHandle, _In_ ULONG Flags, _In_opt_ PWSTR TagName, _In_ PWSTR TagSubName){ /* TODO */ UNIMPLEMENTED; return 0;}
NTSTATUSNTAPIRtlWalkHeap(IN HANDLE HeapHandle, IN PVOID HeapEntry){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
PVOIDNTAPIRtlProtectHeap(IN PVOID HeapHandle, IN BOOLEAN ReadOnly){ UNIMPLEMENTED; return NULL;}
NTSTATUSNTAPIRtlSetHeapInformation(IN HANDLE HeapHandle OPTIONAL, IN HEAP_INFORMATION_CLASS HeapInformationClass, IN PVOID HeapInformation, IN SIZE_T HeapInformationLength){ /* Setting heap information is not really supported except for enabling LFH */ if (HeapInformationClass == HeapCompatibilityInformation) { /* Check buffer length */ if (HeapInformationLength < sizeof(ULONG)) { /* The provided buffer is too small */ return STATUS_BUFFER_TOO_SMALL; }
/* Check for a special magic value for enabling LFH */ if (*(PULONG)HeapInformation != 2) { return STATUS_UNSUCCESSFUL; }
DPRINT1("RtlSetHeapInformation() needs to enable LFH\n"); return STATUS_SUCCESS; }
return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlQueryHeapInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapInformationClass, PVOID HeapInformation, SIZE_T HeapInformationLength, PSIZE_T ReturnLength OPTIONAL){ PHEAP Heap = (PHEAP)HeapHandle;
/* Only HeapCompatibilityInformation is supported */ if (HeapInformationClass == HeapCompatibilityInformation) { /* Set result length */ if (ReturnLength) *ReturnLength = sizeof(ULONG);
/* Check buffer length */ if (HeapInformationLength < sizeof(ULONG)) { /* It's too small, return needed length */ return STATUS_BUFFER_TOO_SMALL; }
/* Return front end heap type */ *(PULONG)HeapInformation = Heap->FrontEndHeapType;
return STATUS_SUCCESS; }
return STATUS_UNSUCCESSFUL;}
/* @implemented */ULONGNTAPIRtlMultipleAllocateHeap(IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size, IN ULONG Count, OUT PVOID *Array){ ULONG Index; EXCEPTION_RECORD ExceptionRecord;
for (Index = 0; Index < Count; ++Index) { Array[Index] = RtlAllocateHeap(HeapHandle, Flags, Size); if (Array[Index] == NULL) { /* ERROR_NOT_ENOUGH_MEMORY */ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY);
if (Flags & HEAP_GENERATE_EXCEPTIONS) { ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.NumberParameters = 0; ExceptionRecord.ExceptionFlags = 0;
RtlRaiseException(&ExceptionRecord); } break; } }
return Index;}
/* @implemented */ULONGNTAPIRtlMultipleFreeHeap(IN PVOID HeapHandle, IN ULONG Flags, IN ULONG Count, OUT PVOID *Array){ ULONG Index;
for (Index = 0; Index < Count; ++Index) { if (Array[Index] == NULL) continue;
_SEH2_TRY { if (!RtlFreeHeap(HeapHandle, Flags, Array[Index])) { /* ERROR_INVALID_PARAMETER */ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); break; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* ERROR_INVALID_PARAMETER */ RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); break; } _SEH2_END; }
return Index;}
/* * Info: * - https://securityxploded.com/enumheaps.php * - https://evilcodecave.wordpress.com/2009/04/14/rtlqueryprocessheapinformation-as-anti-dbg-trick/ */struct _DEBUG_BUFFER;
NTSTATUSNTAPIRtlQueryProcessHeapInformation( IN struct _DEBUG_BUFFER *DebugBuffer){ UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED;}
/* EOF */