a subtle change in behavior...

P

Peter Oliphant

I use to make the statement that you could always replace any 'private' or
'public' access with 'public' and the code would still compile. This is no
longer the case.

It turns out that in /clr it is not allowed (as far as I can tell) to reduce
the access of a virtual method defined in a base class. Thus, if one
declares a virtual method in a base class as 'protected' and then proceeded
to create derived classes with the virtual method polymorphed as 'protected'
methods, then changing the base class version to 'public' will now cause all
the derived classes to generate compiler errors! This is true of virtual
abstract methods as well...

Why is limiting access of a polymorphed virtual methods no longer allowed? I
speak here of ref classes compiled using /clr. It seems it would be a very
good practice to create a base class with methods a child could use but
might not want this method changeable in any derived grand-children. This is
especially true with abstract methods, as it is very reasonable to create a
'line' of derived classes with a particular abstract method fixed. This
appears to no longer be the case, since if you make the abstract method
accessible to its immediate child it must now also be polymorphable by all
the grand-children, and all the grand-children and children of any other
'lines' derived from the base class...

I thought that was the reason for not making everythin 'public' in the first
place - the abilty to limit access. And it's always been a good rule to
apply the 'principle of least accessibility'. Now this can be a real chore
to maintain, as its possible (like in the project I'm doing now) to have
hundreds of 'concrete' classes derived from a given base class, and then
watch out if you need to change the accessibility of a method or member in
the base class...!

I'm guess there are new ways to do this, but I haven't seen any mention of
this...

[==P==]
 
G

Guest

Peter... Not a full answser, but you can seal a virtual method:

public: virtual void Method() sealed {
Console::WriteLine("Child");
}

and this will keep a grandchild class from _overriding_ the method.

JAL
 
P

Peter Oliphant

Hi JAL,

Cool, yes, that does answer part of my question, thanks! : )

[==P==]
 
B

Brandon Bray [MSFT]

Peter said:
Why is limiting access of a polymorphed virtual methods no longer
allowed?

When considering assembly boundaries, there's a potential security hole when
derived classes can override functions they do not have permission to call.
Consider the case where I have a hosted ASP.NET server with some library
that I provide. Some of the public types in that library have 'internal'
virtual functions (that is they are public inside the assembly and private
outside the assembly).

Now, if I'm allowed to put code up on that server that uses that library, if
I'm allowed to override the internal virtual functions outside of the
library's assembly, I can dramatically change the behavior. It's not hard to
imagine how that could lead to unintended consequences, which can then
easily lead to a security exploit.

Thus, in CLR 1.1, the ability for a function to mark that it cannot be
overriden if the overriding class has no permission to call it was added. We
chose to make that behavior the default in C++. After all, we don't want
security holes in libraries generated by C++. :)
I'm guess there are new ways to do this, but I haven't seen any mention
of this...

As already mentioned, the sealed keyword solves many of the issues here.

Hope that helps!
 
P

Peter Oliphant

Hi Brandon,

From my reading of it it, it looks to me like your post is describing why
limiting accessiblity is a good thing. Note that I asked why reducing access
is NOT allowed (meaning in the traditional way, such as changing the
derived polymorphed method to a lesser-accessible state (e.g., changing it
from 'protected' to 'private', without using a different keyword, such as
'sealed'). So it looks like we are in violent agreement...lol

In a nutshell, I'm not sure why the error occurs below (that is, why the old
syntax was not only changed, but is not even legal):

ref class baseClass
{
protected: // access
virtual void f( ) {}
} ;

// old syntax
ref class derivedClassA : public baseClass
{
private : // reducing access
virtual void f( ) override {} // error!
} ;

// new syntax
ref class derivedClassB : public baseClass
{
protected : // same access
virtual void f( ) sealed {} // no error
} ;

Why aren't BOTH of these syntax's allowed? It seems to me that an old syntax
should only be removed if it conflicts or is ambiguous with the new syntax,
which this doesn't seem to be an example of. And before anyone argues it
best to only have one way to do something, then explain why this isn't the
case in the following example:

int x(3) ; //is the same as
int x = 3 ;

[assuming one can't overload the 'int constructor' or '= operator'...]

if ( condition ) {a=1} else {a=2} // is the same as
a = (condtion) ? (1) : (2) ;

[granted these will likely produce different object code, but are
functionally the same]

if ( bool_a == false){} // is the same as
if ( !bool_a ) {}

if ( bool_a == true ){} // is the same as
if ( bool_a ) {}

So, more than one syntax for the same result is not exactly foreign to
C++... : )

[==P==]
 
T

Tamas Demjen

Peter said:
From my reading of it it, it looks to me like your post is describing why
limiting accessiblity is a good thing. Note that I asked why reducing access
is NOT allowed (meaning in the traditional way, such as changing the
derived polymorphed method to a lesser-accessible state (e.g., changing it
from 'protected' to 'private', without using a different keyword, such as
'sealed'). So it looks like we are in violent agreement...lol

Reducing access doesn't make any sense, since you can access the method
via the baseClass interface. One could cast derivedClass^ to baseClass^
and simply call the method. You can *never* reduce accessibility, you
can only make members more accessible. And it's equally true for native
and managed code.

ref class baseClass
{
protected: // access
virtual void f( ) {}
} ;

// old syntax
ref class derivedClassA : public baseClass
{
private : // reducing access
virtual void f( ) override {} // error!
} ;

I don't follow you. This is the new syntax, not the old one. It's
C++/CLI, it has the ref class keyword.

Brandon explained why private virtual functions are not allowed, unless
they're sealed. If a private virtual function is not sealed, it's a
security hole, because the client could override it. The client
shouldn't override anything private, because private methods are meant
to be none of the client's business. In native C++ it's not an issue,
because without a header file it's extremely hard to reverse engineer
what a DLL may contain. Due to the reflection, .NET DLLs are self
descriptive, and therefore public and protected members are widely open
to the public eye. What you want to hide must go to the private section.

In other words, in native C++ the protected and private keywords don't
actually hide anything from the public. You can override protected and
private virtual functions equally, only you can't call private ones from
a descentant. In .NET, the meaning of private is different. The private
keyword doesn't just disable the calling of a function, it also disables
override. By the way, .NET has more access types than standard C++. In
addition to public, protected and private, there are 3 more. Take a look
at those:

public private (C# internal, VB Friend WithEvents)
public protected (C# protected internal, VB Protected Friend WithEvents)
protected private (no equivalent in C# or VB)
// new syntax
ref class derivedClassB : public baseClass
{
protected : // same access
virtual void f( ) sealed {} // no error
} ;

This method is protected, so it's valid, regardless of the sealed keyword.

Tom
 
V

Vladimir Nesterovsky

Why is limiting access of a polymorphed virtual methods no longer
When considering assembly boundaries, there's a potential security hole
when derived classes can override functions they do not have permission to
call. Consider the case where I have a hosted ASP.NET server with some
library that I provide. Some of the public types in that library have
'internal' virtual functions (that is they are public inside the assembly
and private outside the assembly).

Now, if I'm allowed to put code up on that server that uses that library,
if I'm allowed to override the internal virtual functions outside of the
library's assembly, I can dramatically change the behavior. It's not hard
to imagine how that could lead to unintended consequences, which can then
easily lead to a security exploit.

I think these arguments are not perfect.
Contrary, one can argue for opposite opinion:

1. At a point where one inherits a class, he becomes a class designer. He
responsible for new services that his class provides. Whatever he does, it
doesn't harm existing code.

2. Designer of a base class can reasoning as following. I want to create a
method, which will be inaccessible to a class clients, so I choice to create
private method. On the other hand I want to allow to derived classes to plug
their logic in my execution chain, so I define my method as virtual. The
conscious decision of base class designer is to create private virtual
method.
 
B

Brandon Bray [MSFT]

Vladimir said:
I think these arguments are not perfect.

Thanks for offering alternative points of view. I certainly appreciate the
constructive feedback.
Contrary, one can argue for opposite opinion:

1. At a point where one inherits a class, he becomes a class designer.
He responsible for new services that his class provides. Whatever he
does, it doesn't harm existing code.

I completely agree with this, and in many ways I think this statement proves
my point. If the base class has a private function, then the base classes
abstraction does not want that function exposed. Any class deriving from it
should not disobey the base class's design. So, the derived class is allowed
to introduce a function of the same name and hide the base class function...
it just can't have access to the base class function.
2. Designer of a base class can reasoning as following. I want to
create a method, which will be inaccessible to a class clients, so I
choice to create private method. On the other hand I want to allow to
derived classes to plug their logic in my execution chain, so I define
my method as virtual. The conscious decision of base class designer is
to create private virtual method.

On this point, I have to disagree. If we're not concerned about assembly
boundaries (or unmanaged DLL boundaries for that matter), then it is
probable that a single entity has control over all of the classes. That
means any security breaches due to visibility violations are the sole
responsibility of that entity. When assemblies are introduced, abstractions
made by one entity must be followed by other entities. Without that
agreement, there is no way to protect abstraction layers and there are real
security concerns as a result.

In general, I'm a stickler for abstraction. Developers need to design
software with the appropriate abstraction. The tools and languages
developers use need to enforce abstractions. If the rules aren't working, it
means the abstractions aren't correct.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top