Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2008-01

Re: One more issue with string function results (hopefully, the last)

5 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Peter Vreman
Show quoted text 38 lines
> Hello, > > Ran into it while compiling Lazarus. It appears that, while many > sophisticated checks are done in tcallnode.maybe_create_funcret_node, > the obvious case when function result destination is one of the > function's arguments, is not checked. To be honest, I noticed that > earlier for the case of s := Copy(s, ...), but was thiniking that there > is some compiler magic around Copy(). Delphi also appears to be smart > enough to omit temps in case of s := copy(s,...). > Anyway, since copy() assigns its result once before exiting, it works > fine. Things go wrong when the function is tries to access its argument > after modifying the result, like in the attached example. This issue is caused by your own patch to change the ansistring code to use pass the result by reference. Original code (using current 2.2.1) was: # [21] s := foo(s); // This one failes movl -4(%ebp),%eax call P$PROGRAM_FOO$ANSISTRING$$ANSISTRING movl %eax,%ebx leal -4(%ebp),%eax call FPC_ANSISTR_DECR_REF movl %ebx,-4(%ebp) The new code since r9718 is: # [21] s := foo(s); // This one failes leal -4(%ebp),%edx movl -4(%ebp),%eax call P$PROGRAM_FOO$ANSISTRING$$ANSISTRING Here you can see that the result is at the same location as the parameter. In the original code the value is only assigned to the original location after the call. The quick-solution is simple: revert r9718 until it works.

The example that is provided doesn't fail. Because when calling foo the value of -4(%ebp) is loaded in %eax and that value is used in the function foo. The setlength() will create a new string and store that at -4(%ebp) of the calling function. But doesn't change the original parameter value in %eax (and also stored also at (%esp) in foo).

Reply by email

Sergei Gorelkin
to Peter Vreman
Peter Vreman wrote 5 lines
The example that is provided doesn't fail. Because when calling foo the value of -4(%ebp) is loaded in %eax and that value is used in the function foo. The setlength() will create a new string and store that at -4(%ebp) of the calling function. But doesn't change the original parameter value in %eax (and also stored also at (%esp) in foo).

Hmm... It doesn't fail for me either now, although I've seen it failing with my own eyes before submitting it the first time :/

Probably I've oversimplified the example.

The failing function in Lazarus code is CreateRelativePath in components/codetools/fileprocs.pas, being called (second occurrence) from TBaseCompilerOptions.MakeOptionsString (ide/compileroptions.pp). That code is much more complex than the example, and what's happening there is not in the testsuite, because my patch wasn't causing testsuite failures.

I'll be digging further in.

Sergei

Reply by email

Jonas Maebe
to Peter Vreman: The example that is provided doesn't fail. Because when calling foo the value of -4(%ebp)…
Peter Vreman wrote 35 lines
> This issue is caused by your own patch to change the ansistring > code to use pass the result by > reference. > > Original code (using current 2.2.1) was: > > # [21] s := foo(s); // This one failes > movl -4(%ebp),%eax > call P$PROGRAM_FOO$ANSISTRING$$ANSISTRING > movl %eax,%ebx > leal -4(%ebp),%eax > call FPC_ANSISTR_DECR_REF > movl %ebx,-4(%ebp) > > The new code since r9718 is: > > # [21] s := foo(s); // This one failes > leal -4(%ebp),%edx > movl -4(%ebp),%eax > call P$PROGRAM_FOO$ANSISTRING$$ANSISTRING > > > Here you can see that the result is at the same location as the > parameter. In the original code > the value is only assigned to the original location after the call. > > The quick-solution is simple: revert r9718 until it works. The example that is provided doesn't fail. Because when calling foo the value of -4(%ebp) is loaded in %eax and that value is used in the function foo. The setlength() will create a new string and store that at -4(%ebp) of the calling function. But doesn't change the original parameter value in %eax (and also stored also at (%esp) in foo).

It is nevertheless unsafe. The called routine has a const ansistring parameter. The refcount for such parameters is not increased. So when the result (= parameter) is overwritten, things will go wrong if a) the refcount was 1
b) the memory location of the old string is reused

Jonas

Reply by email

Sergei Gorelkin
to Sergei Gorelkin: Hmm... It doesn't fail for me either now, although I've seen it failing with my own eyes…
Sergei Gorelkin wrote
I'll be digging further in.

Here goes. I modified the test so that it fails reliably, and patched the compiler so that is doesn't fail again.
It was a bit tricky to get a string with refcount=1 without telling the compiler to take its address.
As for the patch, I could not invent anything more clever than comparing the assignment target with all function parameters one by one. Also typeconversion nodes may have to be handled.

Reply by email

Peter Vreman
Sergei Gorelkin wrote 12 lines
Sergei Gorelkin wrote: > > I'll be digging further in. > Here goes. I modified the test so that it fails reliably, and patched the compiler so that is doesn't fail again. It was a bit tricky to get a string with refcount=1 without telling the compiler to take its address. As for the patch, I could not invent anything more clever than comparing the assignment target with all function parameters one by one. Also typeconversion nodes may have to be handled.

This patch is not good enough. Like you already say typeconversions are not handled. And what about subscriptions or dereferences. Minor remark is the name of the method which is too generic and not making explicit what it does.

Reply by email